From 6a2d0147fb41c184ac397e245b00dabb4e235cc3 Mon Sep 17 00:00:00 2001 From: "Eyo O. Eyo" <7893459+eokoneyo@users.noreply.github.com> Date: Thu, 1 Aug 2024 15:32:19 +0200 Subject: [PATCH 1/8] [Reporting] upgrade puppeteer to v22.13.1 (#189485) ## Summary Update for puppeteer, the following changeset updates puppeteer to version `22.13.1`. The chromium version required for this version of puppeteer is `126.0.6478.182` from revision `1300313`, as such the chromium binary included for windows and darwin platforms either match or were the closest revision to the expectation. The linux headless binary was built from commit `5b5d8292ddf182f8b2096fa665b473b6317906d5` of the same revision. ### How to verify linux headless build - clone the following repo https://github.com/tsullivan/kibana-dev-docker - pull this particular PR - follow the steps outlined in the repo, replacing any occurrence of `kibana-8.13.0-SNAPSHOT-linux-aarch64.tar.gz` from the repo above's step with the output of running build on this changeset. - before running step 4, modify the `kibana.yml` file from the `kibana-dev-docker` repo and include the following so we might be able to verify the version of chromium running; ```yaml logging.loggers: - name: plugins.reporting level: debug ``` - complete the steps outlined in the README, you'll have a linux distro of kibana running on port `5601` - Attempt creating exports of PDF and PNG reports, in dashboard, canvas, and visualizations, on report creation attempt we would see a log output that prints out the chromium version exactly matching this; Screenshot 2024-07-30 at 16 07 10 --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Elastic Machine --- package.json | 2 +- .../chromium/driver_factory/args.test.ts | 17 +- .../browsers/chromium/driver_factory/args.ts | 7 +- .../server/browsers/chromium/paths.ts | 34 ++-- yarn.lock | 189 ++++++++---------- 5 files changed, 109 insertions(+), 140 deletions(-) diff --git a/package.json b/package.json index 1ec0227c9c3d1..12e7a18f50c8f 100644 --- a/package.json +++ b/package.json @@ -1141,7 +1141,7 @@ "pretty-ms": "6.0.0", "prop-types": "^15.8.1", "proxy-from-env": "1.0.0", - "puppeteer": "22.8.1", + "puppeteer": "22.13.1", "query-string": "^6.13.2", "rbush": "^3.0.1", "re-resizable": "^6.9.9", diff --git a/x-pack/plugins/screenshotting/server/browsers/chromium/driver_factory/args.test.ts b/x-pack/plugins/screenshotting/server/browsers/chromium/driver_factory/args.test.ts index 6e955fa3928aa..d66386deb1669 100644 --- a/x-pack/plugins/screenshotting/server/browsers/chromium/driver_factory/args.test.ts +++ b/x-pack/plugins/screenshotting/server/browsers/chromium/driver_factory/args.test.ts @@ -7,13 +7,10 @@ import os from 'os'; import { args } from './args'; -import { getChromiumPackage } from '../../../utils'; // Since chromium v111 headless mode in arm based macs is not working with `--disable-gpu` // This is a known issue: headless uses swiftshader by default and swiftshader's support for WebGL is currently disabled on Arm pending the resolution of https://issuetracker.google.com/issues/165000222. -// As a workaround, we force hardware GL drivers on mac. -// The best way to do this starting with v112 is by passing --enable-gpu, -// v111 and older versions should work with --use-angle. +// As a workaround, we pass --enable-gpu to stop forcing swiftshader, see https://issues.chromium.org/issues/40256775#comment4 describe('headless webgl arm mac workaround', () => { const originalPlatform = process.platform; afterEach(() => { @@ -37,21 +34,15 @@ describe('headless webgl arm mac workaround', () => { expect(flags.includes(`--disable-gpu`)).toBe(true); }); - test("doesn't disable gpu when on an arm mac, adds --use-angle", () => { + test("doesn't disable gpu when on an arm mac, adds --enable-gpu", () => { simulateEnv('darwin', 'arm64'); const flags = args({ userDataDir: '/', proxy: { enabled: false }, }); + expect(flags.includes(`--disable-gpu`)).toBe(false); - expect(flags.includes(`--use-angle`)).toBe(true); - - // if you're updating this, then you're likely updating chromium - // please double-check that the --use-angle flag is still needed for arm macs - // instead of --use-angle you may need --enable-gpu - expect(getChromiumPackage().binaryChecksum).toBe( - 'e5d4ccdbf3b66532c7c0973be2d5bbd3189079646ced55fe4db61d8e7e7bfc7e' - ); // just putting this here so that someone updating the chromium version will see this comment + expect(flags.includes(`--enable-gpu`)).toBe(true); }); }); diff --git a/x-pack/plugins/screenshotting/server/browsers/chromium/driver_factory/args.ts b/x-pack/plugins/screenshotting/server/browsers/chromium/driver_factory/args.ts index fc1bf51d77493..2337e452cdc94 100644 --- a/x-pack/plugins/screenshotting/server/browsers/chromium/driver_factory/args.ts +++ b/x-pack/plugins/screenshotting/server/browsers/chromium/driver_factory/args.ts @@ -73,12 +73,11 @@ export const args = ({ flags.push('--no-sandbox'); } - // Since chromium v111 headless mode in arm based macs is not working with `--disable-gpu` + // Headless mode in arm based macs is not working with `--disable-gpu` // This is a known issue: headless uses swiftshader by default and swiftshader's support for WebGL is currently disabled on Arm pending the resolution of https://issuetracker.google.com/issues/165000222. - // As a workaround, we force hardware GL drivers on mac: v111 and older versions should work with --use-angle. - // The best way to do this when the issue is resolved will be to pass --enable-gpu, + // As a workaround, we pass --enable-gpu to stop forcing swiftshader, see https://issues.chromium.org/issues/40256775#comment4 if (os.arch() === 'arm64' && process.platform === 'darwin') { - flags.push('--use-angle'); + flags.push('--enable-gpu'); } else { flags.push('--disable-gpu'); } diff --git a/x-pack/plugins/screenshotting/server/browsers/chromium/paths.ts b/x-pack/plugins/screenshotting/server/browsers/chromium/paths.ts index 21dde6af6b7de..ba82fe21734e4 100644 --- a/x-pack/plugins/screenshotting/server/browsers/chromium/paths.ts +++ b/x-pack/plugins/screenshotting/server/browsers/chromium/paths.ts @@ -44,10 +44,10 @@ export class ChromiumArchivePaths { platform: 'darwin', architecture: 'x64', archiveFilename: 'chrome-mac.zip', - archiveChecksum: 'f69bb2f5f402aa2bdd28ccab3a9b36857d1591a1f443fa5b8865df644805cb96', - binaryChecksum: 'c6289a1a1d45021b2259ea0c9ae65ea37199452b5457831680a5e192a3f19add', + archiveChecksum: 'fa8004f3c8c5574c089c901e48429d1b01720bf3dd25e05ac56c41d0ab470c10', + binaryChecksum: '56f25cb6881e5c2b1aac0d8e87630517d1af8effdc9319d35f872add048df1ca', binaryRelativePath: 'chrome-mac/Chromium.app/Contents/MacOS/Chromium', - revision: 1274550, + revision: 1300317, // 1300313 is not available for Mac_x64 location: 'common', archivePath: 'Mac', isPreInstalled: false, @@ -56,10 +56,10 @@ export class ChromiumArchivePaths { platform: 'darwin', architecture: 'arm64', archiveFilename: 'chrome-mac.zip', - archiveChecksum: '55d8cfe56d4461645a663de263ae670f78cc69b69aee16a62c47f361fa9a62f2', - binaryChecksum: 'e5d4ccdbf3b66532c7c0973be2d5bbd3189079646ced55fe4db61d8e7e7bfc7e', + archiveChecksum: 'bea49fd3ccd6aaccd7cdc4df38306f002a2934aaa2c044f3b5a3272b31ec77ca', + binaryChecksum: '4c55d9e47deb1179c377c9785afdcdb5f3d3f351bff62b414d43e32ff195bd55', binaryRelativePath: 'chrome-mac/Chromium.app/Contents/MacOS/Chromium', - revision: 1274557, // 1274542 is not available for Mac_Arm + revision: 1300314, // 1300313 is not available for Mac_Arm location: 'common', archivePath: 'Mac_Arm', isPreInstalled: false, @@ -67,22 +67,22 @@ export class ChromiumArchivePaths { { platform: 'linux', architecture: 'x64', - archiveFilename: 'chromium-46cf136-locales-linux_x64.zip', - archiveChecksum: '8ea5f2d72352230f7927725a6ffd6d5fb462a0c8ad76e320003748b62df6d221', - binaryChecksum: 'e7f8109ef7535dab500166b335524dfa4e92324fa31a2a61f510a5fa5afc2eee', + archiveFilename: 'chromium-5b5d829-locales-linux_x64.zip', + archiveChecksum: '799e8fd5f47ea70b8a3972d39b2617c9cbebc7fc433a89251dae312a7c77534b', + binaryChecksum: '216b8f7ff9b41e985397342c2df54e4f8e07a01a3b8a929f39b9a10931d26ff5', binaryRelativePath: 'headless_shell-linux_x64/headless_shell', - revision: 1274542, + revision: 1300313, location: 'custom', isPreInstalled: true, }, { platform: 'linux', architecture: 'arm64', - archiveFilename: 'chromium-46cf136-locales-linux_arm64.zip', - archiveChecksum: '7d01fe8a78a019cf714c913d37353f85d54c4c7d4fde91d6c96605d259a66414', - binaryChecksum: 'd1a8b8708dc5ced8a9c526a0d823a4074325573f9c06fabe14e18fd3a36691c9', + archiveFilename: 'chromium-5b5d829-locales-linux_arm64.zip', + archiveChecksum: '961e20c45c61f8e948efdc4128bb17c23217bbcb28537f270ccf5bf0826981e7', + binaryChecksum: 'fc4027fb6b1c96bef9374d5d9f791097fae2ec2ddc4e0134167075bd52d1458f', binaryRelativePath: 'headless_shell-linux_arm64/headless_shell', - revision: 1274542, + revision: 1300313, location: 'custom', isPreInstalled: true, }, @@ -90,10 +90,10 @@ export class ChromiumArchivePaths { platform: 'win32', architecture: 'x64', archiveFilename: 'chrome-win.zip', - archiveChecksum: 'd5e9691fb9d378ae13c8956be0d9eb45674d033e8b38125ace2f2fdd458e5ca0', - binaryChecksum: '4d8f95e4f218fc3010ab2f0d8f8674f16d554622218e73f9a7c8a22dbba2e078', + archiveChecksum: '27a2ed1473cefc6f48ff5665faa1fbcc69ef5be47ee21777a60e87c8379fdd93', + binaryChecksum: 'd603401a5e6f8bd734b329876e4221a4d24a1999f14df6e32eeb5e6a72520d96', binaryRelativePath: path.join('chrome-win', 'chrome.exe'), - revision: 1274557, + revision: 1300320, // 1300313 is not available for win32 location: 'common', archivePath: 'Win', isPreInstalled: true, diff --git a/yarn.lock b/yarn.lock index 0fc3cbe39119a..9623b2b9378f3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8198,19 +8198,19 @@ resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" integrity sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA= -"@puppeteer/browsers@2.2.3": - version "2.2.3" - resolved "https://registry.yarnpkg.com/@puppeteer/browsers/-/browsers-2.2.3.tgz#ad6b79129c50825e77ddaba082680f4dad0b674e" - integrity sha512-bJ0UBsk0ESOs6RFcLXOt99a3yTDcOKlzfjad+rhFwdaG1Lu/Wzq58GHYCDTlZ9z6mldf4g+NTb+TXEfe0PpnsQ== +"@puppeteer/browsers@2.2.4": + version "2.2.4" + resolved "https://registry.yarnpkg.com/@puppeteer/browsers/-/browsers-2.2.4.tgz#4307245d881aa5a79743050be66568bad0f6ffbb" + integrity sha512-BdG2qiI1dn89OTUUsx2GZSpUzW+DRffR1wlMJyKxVHYrhnKoELSDxDd+2XImUkuWPEKk76H5FcM/gPFrEK1Tfw== dependencies: - debug "4.3.4" - extract-zip "2.0.1" - progress "2.0.3" - proxy-agent "6.4.0" - semver "7.6.0" - tar-fs "3.0.5" - unbzip2-stream "1.4.3" - yargs "17.7.2" + debug "^4.3.5" + extract-zip "^2.0.1" + progress "^2.0.3" + proxy-agent "^6.4.0" + semver "^7.6.2" + tar-fs "^3.0.6" + unbzip2-stream "^1.4.3" + yargs "^17.7.2" "@redocly/ajv@^8.11.0": version "8.11.0" @@ -14124,14 +14124,14 @@ chromedriver@^126.0.3: proxy-from-env "^1.1.0" tcp-port-used "^1.0.2" -chromium-bidi@0.5.19: - version "0.5.19" - resolved "https://registry.yarnpkg.com/chromium-bidi/-/chromium-bidi-0.5.19.tgz#e4f4951b7d9b20d668d6b387839f7b7bf2d69ef4" - integrity sha512-UA6zL77b7RYCjJkZBsZ0wlvCTD+jTjllZ8f6wdO4buevXgTZYjV+XLB9CiEa2OuuTGGTLnI7eN9I60YxuALGQg== +chromium-bidi@0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/chromium-bidi/-/chromium-bidi-0.6.1.tgz#533612dd166b7b36a8ba8b90685ad2fa0c98d064" + integrity sha512-kSxJRj0VgtUKz6nmzc2JPfyfJGzwzt65u7PqhPHtgGQUZLF5oG+ST6l6e5ONfStUMAlhSutFCjaGKllXZa16jA== dependencies: mitt "3.0.1" urlpattern-polyfill "10.0.0" - zod "3.22.4" + zod "3.23.8" ci-info@^2.0.0: version "2.0.0" @@ -14813,16 +14813,6 @@ core-util-is@1.0.2, core-util-is@^1.0.2, core-util-is@~1.0.0: resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= -cosmiconfig@9.0.0: - version "9.0.0" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-9.0.0.tgz#34c3fc58287b915f3ae905ab6dc3de258b55ad9d" - integrity sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg== - dependencies: - env-paths "^2.2.1" - import-fresh "^3.3.0" - js-yaml "^4.1.0" - parse-json "^5.2.0" - cosmiconfig@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-6.0.0.tgz#da4fee853c52f6b1e6935f41c1a2fc50bd4a9982" @@ -14845,6 +14835,16 @@ cosmiconfig@^7.0.0, cosmiconfig@^7.0.1: path-type "^4.0.0" yaml "^1.10.0" +cosmiconfig@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-9.0.0.tgz#34c3fc58287b915f3ae905ab6dc3de258b55ad9d" + integrity sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg== + dependencies: + env-paths "^2.2.1" + import-fresh "^3.3.0" + js-yaml "^4.1.0" + parse-json "^5.2.0" + cp-file@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/cp-file/-/cp-file-7.0.0.tgz#b9454cfd07fe3b974ab9ea0e5f29655791a9b8cd" @@ -15669,10 +15669,10 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.9: dependencies: ms "2.0.0" -debug@4, debug@4.3.4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4: - version "4.3.4" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" - integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== +debug@4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@^4.3.5: + version "4.3.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.6.tgz#2ab2c38fbaffebf8aa95fdfe6d88438c7a13c52b" + integrity sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg== dependencies: ms "2.1.2" @@ -15683,6 +15683,13 @@ debug@4.3.1: dependencies: ms "2.1.2" +debug@4.3.4: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + debug@^3.0.0, debug@^3.1.0, debug@^3.2.7: version "3.2.7" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" @@ -16123,10 +16130,10 @@ detective@^5.0.2: defined "^1.0.0" minimist "^1.1.1" -devtools-protocol@0.0.1273771: - version "0.0.1273771" - resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.1273771.tgz#46aeb5db41417e2c2ad3d8367c598c975290b1a5" - integrity sha512-QDbb27xcTVReQQW/GHJsdQqGKwYBE7re7gxehj467kKP2DKuYBUj6i2k5LRiAC66J1yZG/9gsxooz/s9pcm0Og== +devtools-protocol@0.0.1299070: + version "0.0.1299070" + resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.1299070.tgz#b3e4cf0b678a46f0f907ae6e07e03ad3a53c00df" + integrity sha512-+qtL3eX50qsJ7c+qVyagqi7AWMoQCBGNfoyJZMwm/NSXVqLYbuitrWEEIzxfUmTNy7//Xe8yhMmQ+elj3uAqSg== dezalgo@^1.0.0, dezalgo@^1.0.4: version "1.0.4" @@ -25921,16 +25928,16 @@ process@^0.11.10: resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= -progress@2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" - integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== - progress@^1.1.8: version "1.1.8" resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be" integrity sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74= +progress@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" + integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== + proj4@2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/proj4/-/proj4-2.6.2.tgz#4665d7cbc30fd356375007c2fed53b07dbda1d67" @@ -26053,7 +26060,7 @@ proxy-addr@~2.0.7: forwarded "0.2.0" ipaddr.js "1.9.1" -proxy-agent@6.4.0, proxy-agent@^6.4.0: +proxy-agent@^6.4.0: version "6.4.0" resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-6.4.0.tgz#b4e2dd51dee2b377748aef8d45604c2d7608652d" integrity sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ== @@ -26164,26 +26171,26 @@ pupa@^3.1.0: dependencies: escape-goat "^4.0.0" -puppeteer-core@22.8.1: - version "22.8.1" - resolved "https://registry.yarnpkg.com/puppeteer-core/-/puppeteer-core-22.8.1.tgz#757ec8983ca38486dad8e5464e744f4b8aff5a13" - integrity sha512-m1F6ZSTw1xrJ6xD4B+HonkSNVQmMrRMaqca/ivRcZYJ6jqzOnfEh3QgO9HpNPj6heiAZ2+4IPAU3jdZaTIDnSA== +puppeteer-core@22.13.1: + version "22.13.1" + resolved "https://registry.yarnpkg.com/puppeteer-core/-/puppeteer-core-22.13.1.tgz#3ba03e5ebd98bbbd86e465864cf00314e07309de" + integrity sha512-NmhnASYp51QPRCAf9n0OPxuPMmzkKd8+2sB9Q+BjwwCG25gz6iuNc3LQDWa+cH2tyivmJppLhNNFt6Q3HmoOpw== dependencies: - "@puppeteer/browsers" "2.2.3" - chromium-bidi "0.5.19" - debug "4.3.4" - devtools-protocol "0.0.1273771" - ws "8.17.0" + "@puppeteer/browsers" "2.2.4" + chromium-bidi "0.6.1" + debug "^4.3.5" + devtools-protocol "0.0.1299070" + ws "^8.18.0" -puppeteer@22.8.1: - version "22.8.1" - resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-22.8.1.tgz#d0b96cd722f62a157804dcc3b0d4909e3620bf1d" - integrity sha512-CFgPSKV+iydjO/8/hJVj251Hqp2PLcIa70j6H7sYqkwM8YJ+D3CA74Ufuj+yKtvDIntQPB/nLw4EHrHPcHOPjw== +puppeteer@22.13.1: + version "22.13.1" + resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-22.13.1.tgz#f8e4217919b438f18adb754e9d8414fef58fb3de" + integrity sha512-PwXLDQK5u83Fm5A7TGMq+9BR7iHDJ8a3h21PSsh/E6VfhxiKYkU7+tvGZNSCap6k3pCNDd9oNteVBEctcBalmQ== dependencies: - "@puppeteer/browsers" "2.2.3" - cosmiconfig "9.0.0" - devtools-protocol "0.0.1273771" - puppeteer-core "22.8.1" + "@puppeteer/browsers" "2.2.4" + cosmiconfig "^9.0.0" + devtools-protocol "0.0.1299070" + puppeteer-core "22.13.1" pure-rand@^6.0.0: version "6.0.2" @@ -28304,13 +28311,6 @@ semver@7.5.4: dependencies: lru-cache "^6.0.0" -semver@7.6.0: - version "7.6.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.0.tgz#1a46a4db4bffcccd97b743b5005c8325f23d4e2d" - integrity sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg== - dependencies: - lru-cache "^6.0.0" - semver@^6.0.0, semver@^6.1.0, semver@^6.1.2, semver@^6.3.0, semver@^6.3.1: version "6.3.1" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" @@ -29967,17 +29967,6 @@ tape@^5.0.1: string.prototype.trim "^1.2.1" through "^2.3.8" -tar-fs@3.0.5: - version "3.0.5" - resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-3.0.5.tgz#f954d77767e4e6edf973384e1eb95f8f81d64ed9" - integrity sha512-JOgGAmZyMgbqpLwct7ZV8VzkEB6pxXFBVErLtb+XCOqzc6w1xiWKI9GVd6bwk68EX7eJ4DWmfXVmq8K2ziZTGg== - dependencies: - pump "^3.0.0" - tar-stream "^3.1.5" - optionalDependencies: - bare-fs "^2.1.1" - bare-path "^2.1.0" - tar-fs@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784" @@ -30811,7 +30800,7 @@ unbox-primitive@^1.0.2: has-symbols "^1.0.3" which-boxed-primitive "^1.0.2" -unbzip2-stream@1.4.3: +unbzip2-stream@^1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz#b0da04c4371311df771cdc215e87f2130991ace7" integrity sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg== @@ -32500,15 +32489,10 @@ write-file-atomic@^4.0.1, write-file-atomic@^4.0.2: imurmurhash "^0.1.4" signal-exit "^3.0.7" -ws@8.17.0: - version "8.17.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.0.tgz#d145d18eca2ed25aaf791a183903f7be5e295fea" - integrity sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow== - -ws@>=8.16.0, ws@^8.2.3, ws@^8.4.2, ws@^8.9.0: - version "8.17.1" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.1.tgz#9293da530bb548febc95371d90f9c878727d919b" - integrity sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ== +ws@>=8.16.0, ws@^8.18.0, ws@^8.2.3, ws@^8.4.2, ws@^8.9.0: + version "8.18.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc" + integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw== ws@^7.3.1, ws@^7.4.2: version "7.5.10" @@ -32702,19 +32686,6 @@ yargs@17.0.1: y18n "^5.0.5" yargs-parser "^20.2.2" -yargs@17.7.2, yargs@^17.0.1, yargs@^17.2.1, yargs@^17.3.1, yargs@^17.7.1, yargs@^17.7.2: - version "17.7.2" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" - integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== - dependencies: - cliui "^8.0.1" - escalade "^3.1.1" - get-caller-file "^2.0.5" - require-directory "^2.1.1" - string-width "^4.2.3" - y18n "^5.0.5" - yargs-parser "^21.1.1" - yargs@^15.0.2, yargs@^15.3.1, yargs@^15.4.1: version "15.4.1" resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" @@ -32732,6 +32703,19 @@ yargs@^15.0.2, yargs@^15.3.1, yargs@^15.4.1: y18n "^4.0.0" yargs-parser "^18.1.2" +yargs@^17.0.1, yargs@^17.2.1, yargs@^17.3.1, yargs@^17.7.1, yargs@^17.7.2: + version "17.7.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" + integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== + dependencies: + cliui "^8.0.1" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.3" + y18n "^5.0.5" + yargs-parser "^21.1.1" + yargs@^3.15.0: version "3.32.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.32.0.tgz#03088e9ebf9e756b69751611d2a5ef591482c995" @@ -32818,12 +32802,7 @@ zod-to-json-schema@^3.22.3, zod-to-json-schema@^3.22.4, zod-to-json-schema@^3.22 resolved "https://registry.yarnpkg.com/zod-to-json-schema/-/zod-to-json-schema-3.23.0.tgz#4fc60e88d3c709eedbfaae3f92f8a7bf786469f2" integrity sha512-az0uJ243PxsRIa2x1WmNE/pnuA05gUq/JB8Lwe1EDCCL/Fz9MgjYQ0fPlyc2Tcv6aF2ZA7WM5TWaRZVEFaAIag== -zod@3.22.4: - version "3.22.4" - resolved "https://registry.yarnpkg.com/zod/-/zod-3.22.4.tgz#f31c3a9386f61b1f228af56faa9255e845cf3fff" - integrity sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg== - -zod@^3.22.3, zod@^3.22.4, zod@^3.23.8: +zod@3.23.8, zod@^3.22.3, zod@^3.22.4, zod@^3.23.8: version "3.23.8" resolved "https://registry.yarnpkg.com/zod/-/zod-3.23.8.tgz#e37b957b5d52079769fb8097099b592f0ef4067d" integrity sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g== From a18d60c8c16a30d3c402feb9387484983b9361dd Mon Sep 17 00:00:00 2001 From: jennypavlova Date: Thu, 1 Aug 2024 15:38:42 +0200 Subject: [PATCH 2/8] [Infra] Remove additional `indexFields` related functions (#189706) Relates to https://github.com/elastic/kibana/issues/180690 ## Summary This PR is a follow-up to https://github.com/elastic/kibana/pull/189541 and removes additional functions related to the `indexFields` that are no longer needed ## Testing Check `metricIndicesExist` and `remoteClustersExist` in the request by changing the settings (in the first video I use metricbeat and no oblt cluster and in the second one I am connected to oblt cluster) https://github.com/user-attachments/assets/59920479-5a42-4a6a-a66a-307ddfc48fd8 https://github.com/user-attachments/assets/ef04120c-6873-4b0c-813c-388147c2b741 --- .../source_configuration.ts | 9 --- .../lib/adapters/fields/adapter_types.ts | 23 -------- .../fields/framework_fields_adapter.ts | 47 --------------- .../infra/server/lib/adapters/fields/index.ts | 8 --- .../infra/server/lib/domains/fields_domain.ts | 30 ---------- .../infra/server/lib/infra_types.ts | 2 - .../infra/server/plugin.ts | 5 -- .../server/routes/metrics_sources/index.ts | 57 ++++++------------- 8 files changed, 18 insertions(+), 163 deletions(-) delete mode 100644 x-pack/plugins/observability_solution/infra/server/lib/adapters/fields/adapter_types.ts delete mode 100644 x-pack/plugins/observability_solution/infra/server/lib/adapters/fields/framework_fields_adapter.ts delete mode 100644 x-pack/plugins/observability_solution/infra/server/lib/adapters/fields/index.ts delete mode 100644 x-pack/plugins/observability_solution/infra/server/lib/domains/fields_domain.ts diff --git a/x-pack/plugins/observability_solution/infra/common/source_configuration/source_configuration.ts b/x-pack/plugins/observability_solution/infra/common/source_configuration/source_configuration.ts index 59e5e653ca538..116c30d8274e3 100644 --- a/x-pack/plugins/observability_solution/infra/common/source_configuration/source_configuration.ts +++ b/x-pack/plugins/observability_solution/infra/common/source_configuration/source_configuration.ts @@ -130,15 +130,6 @@ export interface InfraSourceConfiguration /** * Source status */ -const SourceStatusFieldRuntimeType = rt.type({ - name: rt.string, - type: rt.string, - searchable: rt.boolean, - aggregatable: rt.boolean, - displayable: rt.boolean, -}); - -export type InfraSourceIndexField = rt.TypeOf; export const SourceStatusRuntimeType = rt.type({ logIndicesExist: rt.boolean, diff --git a/x-pack/plugins/observability_solution/infra/server/lib/adapters/fields/adapter_types.ts b/x-pack/plugins/observability_solution/infra/server/lib/adapters/fields/adapter_types.ts deleted file mode 100644 index 60b95acf29e02..0000000000000 --- a/x-pack/plugins/observability_solution/infra/server/lib/adapters/fields/adapter_types.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { InfraPluginRequestHandlerContext } from '../../../types'; - -export interface FieldsAdapter { - getIndexFields( - requestContext: InfraPluginRequestHandlerContext, - indices: string - ): Promise; -} - -export interface IndexFieldDescriptor { - name: string; - type: string; - searchable: boolean; - aggregatable: boolean; - displayable: boolean; -} diff --git a/x-pack/plugins/observability_solution/infra/server/lib/adapters/fields/framework_fields_adapter.ts b/x-pack/plugins/observability_solution/infra/server/lib/adapters/fields/framework_fields_adapter.ts deleted file mode 100644 index 45bb8dc0957d9..0000000000000 --- a/x-pack/plugins/observability_solution/infra/server/lib/adapters/fields/framework_fields_adapter.ts +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { FieldSpec } from '@kbn/data-views-plugin/common'; -import type { InfraPluginRequestHandlerContext } from '../../../types'; -import { isNoSuchRemoteClusterMessage, NoSuchRemoteClusterError } from '../../sources/errors'; -import { KibanaFramework } from '../framework/kibana_framework_adapter'; -import { FieldsAdapter, IndexFieldDescriptor } from './adapter_types'; - -export class FrameworkFieldsAdapter implements FieldsAdapter { - private framework: KibanaFramework; - - constructor(framework: KibanaFramework) { - this.framework = framework; - } - - public async getIndexFields( - requestContext: InfraPluginRequestHandlerContext, - indices: string - ): Promise { - const indexPatternsService = await this.framework.getIndexPatternsServiceWithRequestContext( - requestContext - ); - - try { - // NOTE: Unfortunately getFieldsForWildcard is typed to "any" here in the data plugin, FieldSpec is used below in the map. - const response = await indexPatternsService.getFieldsForWildcard({ - pattern: indices, - allowNoIndex: true, - }); - - return response.map((field: FieldSpec) => ({ - ...field, - displayable: true, - })); - } catch (error) { - if (isNoSuchRemoteClusterMessage(error.message)) { - throw new NoSuchRemoteClusterError(); - } - throw error; - } - } -} diff --git a/x-pack/plugins/observability_solution/infra/server/lib/adapters/fields/index.ts b/x-pack/plugins/observability_solution/infra/server/lib/adapters/fields/index.ts deleted file mode 100644 index 5d7c09c54b8c1..0000000000000 --- a/x-pack/plugins/observability_solution/infra/server/lib/adapters/fields/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export * from './adapter_types'; diff --git a/x-pack/plugins/observability_solution/infra/server/lib/domains/fields_domain.ts b/x-pack/plugins/observability_solution/infra/server/lib/domains/fields_domain.ts deleted file mode 100644 index b80fa9d796021..0000000000000 --- a/x-pack/plugins/observability_solution/infra/server/lib/domains/fields_domain.ts +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { InfraPluginRequestHandlerContext } from '../../types'; -import { FieldsAdapter } from '../adapters/fields'; -import { InfraSourceIndexField, InfraSources } from '../sources'; - -export class InfraFieldsDomain { - constructor( - private readonly adapter: FieldsAdapter, - private readonly libs: { sources: InfraSources } - ) {} - - public async getFields( - requestContext: InfraPluginRequestHandlerContext, - sourceId: string, - indexType: 'METRICS' - ): Promise { - const soClient = (await requestContext.core).savedObjects.client; - const { configuration } = await this.libs.sources.getSourceConfiguration(soClient, sourceId); - - const fields = await this.adapter.getIndexFields(requestContext, configuration.metricAlias); - - return fields; - } -} diff --git a/x-pack/plugins/observability_solution/infra/server/lib/infra_types.ts b/x-pack/plugins/observability_solution/infra/server/lib/infra_types.ts index 8cf0fc81a7321..08cf030a16219 100644 --- a/x-pack/plugins/observability_solution/infra/server/lib/infra_types.ts +++ b/x-pack/plugins/observability_solution/infra/server/lib/infra_types.ts @@ -18,13 +18,11 @@ import { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server'; import { RulesServiceSetup } from '../services/rules'; import { InfraConfig, InfraPluginStartServicesAccessor } from '../types'; import { KibanaFramework } from './adapters/framework/kibana_framework_adapter'; -import { InfraFieldsDomain } from './domains/fields_domain'; import { InfraMetricsDomain } from './domains/metrics_domain'; import { InfraSources } from './sources'; import { InfraSourceStatus } from './source_status'; export interface InfraDomainLibs { - fields: InfraFieldsDomain; logEntries: ILogsSharedLogEntriesDomain; metrics: InfraMetricsDomain; } diff --git a/x-pack/plugins/observability_solution/infra/server/plugin.ts b/x-pack/plugins/observability_solution/infra/server/plugin.ts index 0182d34b76866..61f53b4b9260d 100644 --- a/x-pack/plugins/observability_solution/infra/server/plugin.ts +++ b/x-pack/plugins/observability_solution/infra/server/plugin.ts @@ -23,7 +23,6 @@ import { LOGS_FEATURE_ID, METRICS_FEATURE_ID } from '../common/constants'; import { publicConfigKeys } from '../common/plugin_config_types'; import { LOGS_FEATURE, METRICS_FEATURE } from './features'; import { initInfraServer } from './infra_server'; -import { FrameworkFieldsAdapter } from './lib/adapters/fields/framework_fields_adapter'; import { InfraServerPluginSetupDeps, InfraServerPluginStartDeps } from './lib/adapters/framework'; import { KibanaFramework } from './lib/adapters/framework/kibana_framework_adapter'; import { KibanaMetricsAdapter } from './lib/adapters/metrics/kibana_metrics_adapter'; @@ -33,7 +32,6 @@ import { LOGS_RULES_ALERT_CONTEXT, METRICS_RULES_ALERT_CONTEXT, } from './lib/alerting/register_rule_types'; -import { InfraFieldsDomain } from './lib/domains/fields_domain'; import { InfraMetricsDomain } from './lib/domains/metrics_domain'; import { InfraBackendLibs, InfraDomainLibs } from './lib/infra_types'; import { infraSourceConfigurationSavedObjectType, InfraSources } from './lib/sources'; @@ -210,9 +208,6 @@ export class InfraServerPlugin // and make them available via the request context so we can do away with // the wrapper classes const domainLibs: InfraDomainLibs = { - fields: new InfraFieldsDomain(new FrameworkFieldsAdapter(framework), { - sources, - }), logEntries: plugins.logsShared.logEntries, metrics: new InfraMetricsDomain(new KibanaMetricsAdapter(framework)), }; diff --git a/x-pack/plugins/observability_solution/infra/server/routes/metrics_sources/index.ts b/x-pack/plugins/observability_solution/infra/server/routes/metrics_sources/index.ts index 945e0d684ab5e..3540dac3d311c 100644 --- a/x-pack/plugins/observability_solution/infra/server/routes/metrics_sources/index.ts +++ b/x-pack/plugins/observability_solution/infra/server/routes/metrics_sources/index.ts @@ -24,7 +24,7 @@ import { MetricsSourceStatus, partialMetricsSourceConfigurationReqPayloadRT, } from '../../../common/metrics_sources'; -import { InfraSource, InfraSourceIndexField } from '../../lib/sources'; +import { InfraSource } from '../../lib/sources'; import { InfraPluginRequestHandlerContext } from '../../types'; import { getInfraMetricsClient } from '../../lib/helpers/get_infra_metrics_client'; @@ -42,36 +42,24 @@ export const initMetricsSourceConfigurationRoutes = (libs: InfraBackendLibs) => requestContext: InfraPluginRequestHandlerContext, sourceId: string ): Promise => { - const [metricIndicesExistSettled, indexFieldsSettled] = await Promise.allSettled([ - libs.sourceStatus.hasMetricIndices(requestContext, sourceId), - libs.fields.getFields(requestContext, sourceId, 'METRICS'), - ]); - - /** - * Extract values from promises settlements - */ - const metricIndicesExist = isFulfilled(metricIndicesExistSettled) - ? metricIndicesExistSettled.value - : defaultStatus.metricIndicesExist; - const remoteClustersExist = hasRemoteCluster( - indexFieldsSettled, - metricIndicesExistSettled - ); - - /** - * Report gracefully handled rejections - */ - if (!isFulfilled(indexFieldsSettled)) { - logger.error(indexFieldsSettled.reason); - } - if (!isFulfilled(metricIndicesExistSettled)) { - logger.error(metricIndicesExistSettled.reason); - } + try { + const hasMetricIndices = await libs.sourceStatus.hasMetricIndices(requestContext, sourceId); + return { + metricIndicesExist: hasMetricIndices, + remoteClustersExist: true, + }; + } catch (err) { + logger.error(err); + + if (err instanceof NoSuchRemoteClusterError) { + return defaultStatus; + } - return { - metricIndicesExist, - remoteClustersExist, - }; + return { + metricIndicesExist: false, + remoteClustersExist: true, + }; + } }; framework.registerRoute( @@ -283,12 +271,3 @@ export const initMetricsSourceConfigurationRoutes = (libs: InfraBackendLibs) => const isFulfilled = ( promiseSettlement: PromiseSettledResult ): promiseSettlement is PromiseFulfilledResult => promiseSettlement.status === 'fulfilled'; - -const hasRemoteCluster = (...promiseSettlements: Array>) => { - const isRemoteMissing = promiseSettlements.some( - (settlement) => - !isFulfilled(settlement) && settlement.reason instanceof NoSuchRemoteClusterError - ); - - return !isRemoteMissing; -}; From 93378d9e377265effe5ae182e431253c35c48fbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loix?= Date: Thu, 1 Aug 2024 15:10:00 +0100 Subject: [PATCH 3/8] [Stateful sidenav] Add telemetry (#189275) --- .../__jest__/build_nav_tree.test.tsx | 38 +++++++++ .../chrome/navigation/__jest__/utils.tsx | 5 +- packages/shared-ux/chrome/navigation/index.ts | 2 + .../chrome/navigation/mocks/storybook.ts | 4 +- .../navigation/src/analytics/event_tracker.ts | 43 ++++++++++ .../chrome/navigation/src/analytics/index.ts | 9 ++ .../chrome/navigation/src/services.tsx | 32 +++++--- .../shared-ux/chrome/navigation/src/types.ts | 7 +- .../ui/components/navigation_section_ui.tsx | 39 ++++++++- .../shared-ux/chrome/navigation/tsconfig.json | 1 + .../navigation/public/analytics/index.ts | 9 ++ .../public/analytics/register_event_types.ts | 60 ++++++++++++++ src/plugins/navigation/public/plugin.tsx | 5 +- x-pack/plugins/spaces/common/index.ts | 1 + .../plugins/spaces/common/types/space/v1.ts | 4 +- .../spaces/public/analytics/event_tracker.ts | 80 ++++++++++++++++++ .../plugins/spaces/public/analytics/index.ts | 12 +++ .../analytics/register_analytics_context.ts | 33 ++++++++ .../public/analytics/register_event_types.ts | 82 +++++++++++++++++++ .../public/management/edit_space/index.ts | 1 - .../edit_space/manage_space_page.test.tsx | 51 +++++++++++- .../edit_space/manage_space_page.tsx | 32 ++++++-- .../management/management_service.test.ts | 6 ++ .../public/management/management_service.tsx | 4 + .../management/spaces_management_app.test.tsx | 9 +- .../management/spaces_management_app.tsx | 12 ++- .../nav_control/components/spaces_menu.tsx | 40 +++++++-- .../spaces/public/nav_control/nav_control.tsx | 5 +- .../nav_control/nav_control_popover.test.tsx | 52 +++++++++++- .../nav_control/nav_control_popover.tsx | 3 + x-pack/plugins/spaces/public/plugin.tsx | 10 ++- .../spaces_usage_collector.ts | 9 +- x-pack/plugins/spaces/tsconfig.json | 1 + 33 files changed, 660 insertions(+), 41 deletions(-) create mode 100644 packages/shared-ux/chrome/navigation/src/analytics/event_tracker.ts create mode 100644 packages/shared-ux/chrome/navigation/src/analytics/index.ts create mode 100644 src/plugins/navigation/public/analytics/index.ts create mode 100644 src/plugins/navigation/public/analytics/register_event_types.ts create mode 100644 x-pack/plugins/spaces/public/analytics/event_tracker.ts create mode 100644 x-pack/plugins/spaces/public/analytics/index.ts create mode 100644 x-pack/plugins/spaces/public/analytics/register_analytics_context.ts create mode 100644 x-pack/plugins/spaces/public/analytics/register_event_types.ts diff --git a/packages/shared-ux/chrome/navigation/__jest__/build_nav_tree.test.tsx b/packages/shared-ux/chrome/navigation/__jest__/build_nav_tree.test.tsx index 36e275ced131b..d4e983f86e665 100644 --- a/packages/shared-ux/chrome/navigation/__jest__/build_nav_tree.test.tsx +++ b/packages/shared-ux/chrome/navigation/__jest__/build_nav_tree.test.tsx @@ -12,6 +12,7 @@ import type { ChromeProjectNavigationNode, } from '@kbn/core-chrome-browser'; +import { EventTracker } from '../src/analytics'; import { renderNavigation } from './utils'; describe('builds navigation tree', () => { @@ -135,6 +136,43 @@ describe('builds navigation tree', () => { } }); + test('should track click event', async () => { + const navigateToUrl = jest.fn(); + const reportEvent = jest.fn(); + + const node: ChromeProjectNavigationNode = { + id: 'group1', + title: 'Group 1', + path: 'group1', + defaultIsCollapsed: false, + children: [ + { + id: 'item1', + title: 'Item 1', + href: 'https://foo', + path: 'group1.item1', + }, + ], + }; + + const { findByTestId } = renderNavigation({ + navTreeDef: of({ + body: [node], + }), + services: { navigateToUrl, eventTracker: new EventTracker({ reportEvent }) }, + }); + + const navItem = await findByTestId(/nav-item-group1.item1\s/); + navItem.click(); + + expect(navigateToUrl).toHaveBeenCalled(); + expect(reportEvent).toHaveBeenCalledWith('solutionNav_click_navlink', { + href: undefined, + href_prev: undefined, + id: 'item1', + }); + }); + test('should allow custom onClick handler for links', async () => { const navigateToUrl = jest.fn(); const onClick = jest.fn(); diff --git a/packages/shared-ux/chrome/navigation/__jest__/utils.tsx b/packages/shared-ux/chrome/navigation/__jest__/utils.tsx index 04d67c914ad42..41e5409613aac 100644 --- a/packages/shared-ux/chrome/navigation/__jest__/utils.tsx +++ b/packages/shared-ux/chrome/navigation/__jest__/utils.tsx @@ -19,13 +19,15 @@ import { NavigationProvider } from '../src/services'; import { Navigation } from '../src/ui/navigation'; import type { PanelContentProvider } from '../src/ui'; import { NavigationServices } from '../src/types'; +import { EventTracker } from '../src/analytics'; const activeNodes: ChromeProjectNavigationNode[][] = []; export const getServicesMock = (): NavigationServices => { const navigateToUrl = jest.fn().mockResolvedValue(undefined); - const basePath = { prepend: jest.fn((path: string) => `/base${path}`) }; + const basePath = { prepend: jest.fn((path: string) => `/base${path}`), remove: jest.fn() }; const recentlyAccessed$ = new BehaviorSubject([]); + const eventTracker = new EventTracker({ reportEvent: jest.fn() }); return { basePath, @@ -34,6 +36,7 @@ export const getServicesMock = (): NavigationServices => { navigateToUrl, activeNodes$: of(activeNodes), isSideNavCollapsed: false, + eventTracker, }; }; diff --git a/packages/shared-ux/chrome/navigation/index.ts b/packages/shared-ux/chrome/navigation/index.ts index aadb517ae31bc..77dceca515c47 100644 --- a/packages/shared-ux/chrome/navigation/index.ts +++ b/packages/shared-ux/chrome/navigation/index.ts @@ -9,6 +9,8 @@ export { NavigationKibanaProvider, NavigationProvider } from './src/services'; export { Navigation } from './src/ui'; +export { EventType, FieldType } from './src/analytics'; + export type { NavigationProps } from './src/ui'; export type { PanelComponentProps, PanelContent, PanelContentProvider } from './src/ui'; diff --git a/packages/shared-ux/chrome/navigation/mocks/storybook.ts b/packages/shared-ux/chrome/navigation/mocks/storybook.ts index c34819090c5f4..4a68717a9a5d9 100644 --- a/packages/shared-ux/chrome/navigation/mocks/storybook.ts +++ b/packages/shared-ux/chrome/navigation/mocks/storybook.ts @@ -9,6 +9,7 @@ import { AbstractStorybookMock } from '@kbn/shared-ux-storybook-mock'; import { action } from '@storybook/addon-actions'; import { BehaviorSubject } from 'rxjs'; +import { EventTracker } from '../src/analytics'; import { NavigationServices } from '../src/types'; type Arguments = NavigationServices; @@ -35,11 +36,12 @@ export class StorybookMock extends AbstractStorybookMock<{}, NavigationServices> return { ...params, - basePath: { prepend: (suffix: string) => `/basepath${suffix}` }, + basePath: { prepend: (suffix: string) => `/basepath${suffix}`, remove: () => '' }, navigateToUrl, recentlyAccessed$: params.recentlyAccessed$ ?? new BehaviorSubject([]), activeNodes$: params.activeNodes$ ?? new BehaviorSubject([]), isSideNavCollapsed: true, + eventTracker: new EventTracker({ reportEvent: action('Report event') }), }; } diff --git a/packages/shared-ux/chrome/navigation/src/analytics/event_tracker.ts b/packages/shared-ux/chrome/navigation/src/analytics/event_tracker.ts new file mode 100644 index 0000000000000..4d7a6258986be --- /dev/null +++ b/packages/shared-ux/chrome/navigation/src/analytics/event_tracker.ts @@ -0,0 +1,43 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { AnalyticsServiceStart } from '@kbn/core-analytics-browser'; + +export enum EventType { + CLICK_NAVLINK = 'solutionNav_click_navlink', +} + +export enum FieldType { + ID = 'id', + HREF = 'href', + HREF_PREV = 'href_prev', +} + +export class EventTracker { + constructor(private analytics: Pick) {} + + private track(eventType: string, eventFields: object) { + try { + this.analytics.reportEvent(eventType, eventFields); + } catch (err) { + // eslint-disable-next-line no-console + console.error(`Navigation EventTracker error: ${err.toString()}`); + } + } + + /* + * Track whenever a user clicks on a navigation link in the side nav + */ + public clickNavLink({ id, href, hrefPrev }: { id: string; href?: string; hrefPrev?: string }) { + this.track(EventType.CLICK_NAVLINK, { + [FieldType.ID]: id, + [FieldType.HREF]: href, + [FieldType.HREF_PREV]: hrefPrev, + }); + } +} diff --git a/packages/shared-ux/chrome/navigation/src/analytics/index.ts b/packages/shared-ux/chrome/navigation/src/analytics/index.ts new file mode 100644 index 0000000000000..585ac82ca2835 --- /dev/null +++ b/packages/shared-ux/chrome/navigation/src/analytics/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export { EventTracker, EventType, FieldType } from './event_tracker'; diff --git a/packages/shared-ux/chrome/navigation/src/services.tsx b/packages/shared-ux/chrome/navigation/src/services.tsx index 617e5695579f3..4a82aa31dc966 100644 --- a/packages/shared-ux/chrome/navigation/src/services.tsx +++ b/packages/shared-ux/chrome/navigation/src/services.tsx @@ -6,8 +6,9 @@ * Side Public License, v 1. */ -import React, { FC, PropsWithChildren, useContext } from 'react'; +import React, { FC, PropsWithChildren, useContext, useMemo } from 'react'; import useObservable from 'react-use/lib/useObservable'; +import { EventTracker } from './analytics'; import { NavigationKibanaDependencies, NavigationServices } from './types'; @@ -31,19 +32,30 @@ export const NavigationKibanaProvider: FC { const { core, activeNodes$ } = dependencies; - const { chrome, http } = core; + const { chrome, http, analytics } = core; const { basePath } = http; const { navigateToUrl } = core.application; const isSideNavCollapsed = useObservable(chrome.getIsSideNavCollapsed$(), true); - const value: NavigationServices = { - basePath, - recentlyAccessed$: chrome.recentlyAccessed.get$(), - navigateToUrl, - navIsOpen: true, - activeNodes$, - isSideNavCollapsed, - }; + const value: NavigationServices = useMemo( + () => ({ + basePath, + recentlyAccessed$: chrome.recentlyAccessed.get$(), + navigateToUrl, + navIsOpen: true, + activeNodes$, + isSideNavCollapsed, + eventTracker: new EventTracker({ reportEvent: analytics.reportEvent }), + }), + [ + activeNodes$, + analytics.reportEvent, + basePath, + chrome.recentlyAccessed, + isSideNavCollapsed, + navigateToUrl, + ] + ); return {children}; }; diff --git a/packages/shared-ux/chrome/navigation/src/types.ts b/packages/shared-ux/chrome/navigation/src/types.ts index f15bb1a51e117..82d987250a981 100644 --- a/packages/shared-ux/chrome/navigation/src/types.ts +++ b/packages/shared-ux/chrome/navigation/src/types.ts @@ -15,8 +15,9 @@ import type { ChromeProjectNavigationNode, ChromeRecentlyAccessedHistoryItem, } from '@kbn/core-chrome-browser'; +import { EventTracker } from './analytics'; -type BasePathService = Pick; +export type BasePathService = Pick; /** * @internal @@ -35,6 +36,7 @@ export interface NavigationServices { navigateToUrl: NavigateToUrlFn; activeNodes$: Observable; isSideNavCollapsed: boolean; + eventTracker: EventTracker; } /** @@ -56,6 +58,9 @@ export interface NavigationKibanaDependencies { basePath: BasePathService; getLoadingCount$(): Observable; }; + analytics: { + reportEvent: (eventType: string, eventData: object) => void; + }; }; activeNodes$: Observable; } diff --git a/packages/shared-ux/chrome/navigation/src/ui/components/navigation_section_ui.tsx b/packages/shared-ux/chrome/navigation/src/ui/components/navigation_section_ui.tsx index 3d549cf663703..6a53239590d92 100644 --- a/packages/shared-ux/chrome/navigation/src/ui/components/navigation_section_ui.tsx +++ b/packages/shared-ux/chrome/navigation/src/ui/components/navigation_section_ui.tsx @@ -21,8 +21,9 @@ import type { EuiThemeSize, RenderAs } from '@kbn/core-chrome-browser/src/projec import { useNavigation as useServices } from '../../services'; import { isAbsoluteLink, isActiveFromUrl, isAccordionNode } from '../../utils'; -import type { NavigateToUrlFn } from '../../types'; +import type { BasePathService, NavigateToUrlFn } from '../../types'; import { useNavigation } from '../navigation'; +import { EventTracker } from '../../analytics'; import { useAccordionState } from '../hooks'; import { DEFAULT_IS_COLLAPSIBLE, @@ -183,6 +184,8 @@ const getEuiProps = ( treeDepth: number; getIsCollapsed: (path: string) => boolean; activeNodes: ChromeProjectNavigationNode[][]; + eventTracker: EventTracker; + basePath: BasePathService; } ): { navNode: ChromeProjectNavigationNode; @@ -192,7 +195,15 @@ const getEuiProps = ( dataTestSubj: string; spaceBefore?: EuiThemeSize | null; } & Pick => { - const { navigateToUrl, closePanel, treeDepth, getIsCollapsed, activeNodes } = deps; + const { + navigateToUrl, + closePanel, + treeDepth, + getIsCollapsed, + activeNodes, + eventTracker, + basePath, + } = deps; const { navNode, isItem, hasChildren, hasLink } = serializeNavNode(_navNode); const { path, href, onClick: customOnClick, isCollapsible = DEFAULT_IS_COLLAPSIBLE } = navNode; @@ -239,6 +250,14 @@ const getEuiProps = ( href, external: isExternal, onClick: (e) => { + if (href) { + eventTracker.clickNavLink({ + href: basePath.remove(href), + id: navNode.id, + hrefPrev: basePath.remove(window.location.pathname), + }); + } + if (customOnClick) { customOnClick(e); return; @@ -253,6 +272,14 @@ const getEuiProps = ( : undefined; const onClick = (e: React.MouseEvent) => { + if (href) { + eventTracker.clickNavLink({ + href: basePath.remove(href), + id: navNode.id, + hrefPrev: basePath.remove(window.location.pathname), + }); + } + if (customOnClick) { customOnClick(e); return; @@ -293,6 +320,8 @@ function nodeToEuiCollapsibleNavProps( treeDepth: number; getIsCollapsed: (path: string) => boolean; activeNodes: ChromeProjectNavigationNode[][]; + eventTracker: EventTracker; + basePath: BasePathService; } ): { items: Array; @@ -369,7 +398,7 @@ interface Props { export const NavigationSectionUI: FC = React.memo(({ navNode: _navNode }) => { const { activeNodes } = useNavigation(); - const { navigateToUrl } = useServices(); + const { navigateToUrl, eventTracker, basePath } = useServices(); const [items, setItems] = useState(); const { navNode } = useMemo( @@ -394,8 +423,10 @@ export const NavigationSectionUI: FC = React.memo(({ navNode: _navNode }) treeDepth: 0, getIsCollapsed, activeNodes, + eventTracker, + basePath, }); - }, [navNode, navigateToUrl, closePanel, getIsCollapsed, activeNodes]); + }, [navNode, navigateToUrl, closePanel, getIsCollapsed, activeNodes, eventTracker, basePath]); const { items: topLevelItems } = props; diff --git a/packages/shared-ux/chrome/navigation/tsconfig.json b/packages/shared-ux/chrome/navigation/tsconfig.json index d123d451597e6..9936cc9f1b892 100644 --- a/packages/shared-ux/chrome/navigation/tsconfig.json +++ b/packages/shared-ux/chrome/navigation/tsconfig.json @@ -22,6 +22,7 @@ "@kbn/i18n", "@kbn/shared-ux-storybook-mock", "@kbn/core-http-browser", + "@kbn/core-analytics-browser", ], "exclude": [ "target/**/*" diff --git a/src/plugins/navigation/public/analytics/index.ts b/src/plugins/navigation/public/analytics/index.ts new file mode 100644 index 0000000000000..2c51c4e994a95 --- /dev/null +++ b/src/plugins/navigation/public/analytics/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export { registerNavigationEventTypes } from './register_event_types'; diff --git a/src/plugins/navigation/public/analytics/register_event_types.ts b/src/plugins/navigation/public/analytics/register_event_types.ts new file mode 100644 index 0000000000000..d1571f8ff0245 --- /dev/null +++ b/src/plugins/navigation/public/analytics/register_event_types.ts @@ -0,0 +1,60 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { CoreSetup, EventTypeOpts, RootSchema } from '@kbn/core/public'; +import { + FieldType as NavigationFieldType, + EventType as NavigationEventType, +} from '@kbn/shared-ux-chrome-navigation'; + +const fields: Record>> = { + [NavigationFieldType.ID]: { + [NavigationFieldType.ID]: { + type: 'keyword', + _meta: { + description: 'The ID of navigation node.', + }, + }, + }, + [NavigationFieldType.HREF]: { + [NavigationFieldType.HREF]: { + type: 'keyword', + _meta: { + description: 'The href of the navigation node.', + optional: true, + }, + }, + }, + [NavigationFieldType.HREF_PREV]: { + [NavigationFieldType.HREF_PREV]: { + type: 'keyword', + _meta: { + description: 'The previous href before clicking on a navigation node.', + optional: true, + }, + }, + }, +}; + +const eventTypes: Array>> = [ + { + eventType: NavigationEventType.CLICK_NAVLINK, + schema: { + ...fields[NavigationFieldType.ID], + ...fields[NavigationFieldType.HREF], + ...fields[NavigationFieldType.HREF_PREV], + }, + }, +]; + +export function registerNavigationEventTypes(core: CoreSetup) { + const { analytics } = core; + for (const eventType of eventTypes) { + analytics.registerEventType(eventType); + } +} diff --git a/src/plugins/navigation/public/plugin.tsx b/src/plugins/navigation/public/plugin.tsx index bef9b7c3a933c..f58e61c7589f6 100644 --- a/src/plugins/navigation/public/plugin.tsx +++ b/src/plugins/navigation/public/plugin.tsx @@ -33,6 +33,7 @@ import type { import { TopNavMenuExtensionsRegistry, createTopNav } from './top_nav_menu'; import { RegisteredTopNavMenuData } from './top_nav_menu/top_nav_menu_data'; import { SideNavComponent } from './side_navigation'; +import { registerNavigationEventTypes } from './analytics'; export class NavigationPublicPlugin implements @@ -52,7 +53,9 @@ export class NavigationPublicPlugin constructor(private initializerContext: PluginInitializerContext) {} - public setup(_core: CoreSetup): NavigationPublicSetup { + public setup(core: CoreSetup): NavigationPublicSetup { + registerNavigationEventTypes(core); + return { registerMenuItem: this.topNavMenuExtensionsRegistry.register.bind( this.topNavMenuExtensionsRegistry diff --git a/x-pack/plugins/spaces/common/index.ts b/x-pack/plugins/spaces/common/index.ts index 4a767fb403ee2..65342bf2e43f4 100644 --- a/x-pack/plugins/spaces/common/index.ts +++ b/x-pack/plugins/spaces/common/index.ts @@ -18,5 +18,6 @@ export type { GetAllSpacesOptions, GetAllSpacesPurpose, GetSpaceResult, + SolutionView, } from './types/latest'; export { spaceV1 } from './types'; diff --git a/x-pack/plugins/spaces/common/types/space/v1.ts b/x-pack/plugins/spaces/common/types/space/v1.ts index 9f110dc3098e3..9ba2deb09aaa2 100644 --- a/x-pack/plugins/spaces/common/types/space/v1.ts +++ b/x-pack/plugins/spaces/common/types/space/v1.ts @@ -7,6 +7,8 @@ import type { OnBoardingDefaultSolution } from '@kbn/cloud-plugin/common'; +export type SolutionView = OnBoardingDefaultSolution | 'classic'; + /** * A Space. */ @@ -64,7 +66,7 @@ export interface Space { /** * Solution selected for this space. */ - solution?: OnBoardingDefaultSolution | 'classic'; + solution?: SolutionView; } /** diff --git a/x-pack/plugins/spaces/public/analytics/event_tracker.ts b/x-pack/plugins/spaces/public/analytics/event_tracker.ts new file mode 100644 index 0000000000000..ec936d1c1ef20 --- /dev/null +++ b/x-pack/plugins/spaces/public/analytics/event_tracker.ts @@ -0,0 +1,80 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { AnalyticsServiceStart } from '@kbn/core/public'; + +import type { SolutionView } from '../../common'; + +export enum EventType { + SPACE_SOLUTION_CHANGED = 'space_solution_changed', + SPACE_CHANGED = 'space_changed', +} + +export enum FieldType { + ACTION = 'action', + SPACE_ID = 'space_id', + SPACE_ID_PREV = 'space_id_prev', + SOLUTION = 'solution', + SOLUTION_PREV = 'solution_prev', +} + +export class EventTracker { + constructor(private analytics: Pick) {} + + private track(eventType: string, eventFields: object) { + try { + this.analytics.reportEvent(eventType, eventFields); + } catch (err) { + // eslint-disable-next-line no-console + console.error(err); + } + } + + /** + * Track whenever the space "solution" is changed. + */ + public spaceSolutionChanged({ + spaceId, + action, + solution, + solutionPrev, + }: { + spaceId: string; + action: 'create' | 'edit'; + solution: SolutionView; + solutionPrev?: SolutionView; + }) { + this.track(EventType.SPACE_SOLUTION_CHANGED, { + [FieldType.SPACE_ID]: spaceId, + [FieldType.SOLUTION]: solution, + [FieldType.SOLUTION_PREV]: solutionPrev, + [FieldType.ACTION]: action, + }); + } + + /** + * Track whenever the user changes space. + */ + public changeSpace({ + prevSpaceId, + prevSolution, + nextSpaceId, + nextSolution, + }: { + prevSpaceId: string; + prevSolution?: SolutionView; + nextSpaceId: string; + nextSolution?: SolutionView; + }) { + this.track(EventType.SPACE_CHANGED, { + [FieldType.SPACE_ID]: nextSpaceId, + [FieldType.SPACE_ID_PREV]: prevSpaceId, + [FieldType.SOLUTION]: nextSolution, + [FieldType.SOLUTION_PREV]: prevSolution, + }); + } +} diff --git a/x-pack/plugins/spaces/public/analytics/index.ts b/x-pack/plugins/spaces/public/analytics/index.ts new file mode 100644 index 0000000000000..a7d363f988aa2 --- /dev/null +++ b/x-pack/plugins/spaces/public/analytics/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 + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { registerAnalyticsContext } from './register_analytics_context'; + +export { EventTracker } from './event_tracker'; + +export { registerSpacesEventTypes } from './register_event_types'; diff --git a/x-pack/plugins/spaces/public/analytics/register_analytics_context.ts b/x-pack/plugins/spaces/public/analytics/register_analytics_context.ts new file mode 100644 index 0000000000000..8f6f319d3333c --- /dev/null +++ b/x-pack/plugins/spaces/public/analytics/register_analytics_context.ts @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { Observable } from 'rxjs'; +import { map } from 'rxjs'; + +import type { AnalyticsClient } from '@kbn/core-analytics-browser'; + +import type { SolutionView, Space } from '../../common'; + +export interface SpaceMetadata { + spaceSolutionView?: SolutionView; +} + +export function registerAnalyticsContext( + analytics: Pick, + activeSpace: Observable +) { + analytics.registerContextProvider({ + name: 'Spaces Metadata', + context$: activeSpace.pipe(map((space) => ({ spaceSolution: space.solution }))), + schema: { + spaceSolution: { + type: 'keyword', + _meta: { description: 'The Space solution view', optional: true }, + }, + }, + }); +} diff --git a/x-pack/plugins/spaces/public/analytics/register_event_types.ts b/x-pack/plugins/spaces/public/analytics/register_event_types.ts new file mode 100644 index 0000000000000..701fdadb3c4ee --- /dev/null +++ b/x-pack/plugins/spaces/public/analytics/register_event_types.ts @@ -0,0 +1,82 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { CoreSetup, EventTypeOpts, RootSchema } from '@kbn/core/public'; + +import { EventType, FieldType } from './event_tracker'; + +const fields: Record>> = { + [FieldType.SPACE_ID]: { + [FieldType.SPACE_ID]: { + type: 'keyword', + _meta: { + description: 'The ID of the space.', + }, + }, + }, + [FieldType.SPACE_ID_PREV]: { + [FieldType.SPACE_ID_PREV]: { + type: 'keyword', + _meta: { + description: 'The previous ID of the space (before switching space).', + }, + }, + }, + [FieldType.SOLUTION]: { + [FieldType.SOLUTION]: { + type: 'keyword', + _meta: { + description: 'The solution set for the space.', + }, + }, + }, + [FieldType.SOLUTION_PREV]: { + [FieldType.SOLUTION_PREV]: { + type: 'keyword', + _meta: { + description: 'The previous solution value before editing the space.', + optional: true, + }, + }, + }, + [FieldType.ACTION]: { + [FieldType.ACTION]: { + type: 'keyword', + _meta: { + description: 'The user action, either create or edit a space.', + }, + }, + }, +}; + +const eventTypes: Array>> = [ + { + eventType: EventType.SPACE_SOLUTION_CHANGED, + schema: { + ...fields[FieldType.SPACE_ID], + ...fields[FieldType.SOLUTION_PREV], + ...fields[FieldType.SOLUTION], + ...fields[FieldType.ACTION], + }, + }, + { + eventType: EventType.SPACE_CHANGED, + schema: { + ...fields[FieldType.SPACE_ID], + ...fields[FieldType.SPACE_ID_PREV], + ...fields[FieldType.SOLUTION_PREV], + ...fields[FieldType.SOLUTION], + }, + }, +]; + +export function registerSpacesEventTypes(core: CoreSetup) { + const { analytics } = core; + for (const eventType of eventTypes) { + analytics.registerEventType(eventType); + } +} diff --git a/x-pack/plugins/spaces/public/management/edit_space/index.ts b/x-pack/plugins/spaces/public/management/edit_space/index.ts index fa27a38ab5855..78c3b0fc42e04 100644 --- a/x-pack/plugins/spaces/public/management/edit_space/index.ts +++ b/x-pack/plugins/spaces/public/management/edit_space/index.ts @@ -5,5 +5,4 @@ * 2.0. */ -// @ts-ignore export { ManageSpacePage } from './manage_space_page'; diff --git a/x-pack/plugins/spaces/public/management/edit_space/manage_space_page.test.tsx b/x-pack/plugins/spaces/public/management/edit_space/manage_space_page.test.tsx index 2bcf35ccc6cc4..c6aae56a938fc 100644 --- a/x-pack/plugins/spaces/public/management/edit_space/manage_space_page.test.tsx +++ b/x-pack/plugins/spaces/public/management/edit_space/manage_space_page.test.tsx @@ -10,6 +10,7 @@ import { EuiButton } from '@elastic/eui'; import { waitFor } from '@testing-library/react'; import type { ReactWrapper } from 'enzyme'; import React from 'react'; +import { act } from 'react-dom/test-utils'; import { DEFAULT_APP_CATEGORIES } from '@kbn/core/public'; import { notificationServiceMock, scopedHistoryMock } from '@kbn/core/public/mocks'; @@ -20,6 +21,8 @@ import { findTestSubject, mountWithIntl } from '@kbn/test-jest-helpers'; import { ConfirmAlterActiveSpaceModal } from './confirm_alter_active_space_modal'; import { EnabledFeatures } from './enabled_features'; import { ManageSpacePage } from './manage_space_page'; +import type { SolutionView, Space } from '../../../common/types/latest'; +import { EventTracker } from '../../analytics'; import type { SpacesManager } from '../../spaces_manager'; import { spacesManagerMock } from '../../spaces_manager/mocks'; @@ -31,7 +34,7 @@ jest.mock('@elastic/eui/lib/components/overlay_mask', () => { }; }); -const space = { +const space: Space = { id: 'my-space', name: 'My Space', disabledFeatures: [], @@ -48,6 +51,9 @@ featuresStart.getFeatures.mockResolvedValue([ }), ]); +const reportEvent = jest.fn(); +const eventTracker = new EventTracker({ reportEvent }); + describe('ManageSpacePage', () => { beforeAll(() => { Object.defineProperty(window, 'location', { @@ -75,6 +81,7 @@ describe('ManageSpacePage', () => { catalogue: {}, spaces: { manage: true }, }} + eventTracker={eventTracker} allowFeatureVisibility /> ); @@ -124,6 +131,7 @@ describe('ManageSpacePage', () => { }} allowFeatureVisibility solutionNavExperiment={Promise.resolve(true)} + eventTracker={eventTracker} /> ); @@ -154,6 +162,7 @@ describe('ManageSpacePage', () => { spaces: { manage: true }, }} allowFeatureVisibility + eventTracker={eventTracker} /> ); @@ -180,6 +189,7 @@ describe('ManageSpacePage', () => { }} allowFeatureVisibility solutionNavExperiment={Promise.resolve(false)} + eventTracker={eventTracker} /> ); @@ -209,6 +219,7 @@ describe('ManageSpacePage', () => { catalogue: {}, spaces: { manage: true }, }} + eventTracker={eventTracker} allowFeatureVisibility /> ); @@ -238,6 +249,7 @@ describe('ManageSpacePage', () => { catalogue: {}, spaces: { manage: true }, }} + eventTracker={eventTracker} allowFeatureVisibility={false} /> ); @@ -258,6 +270,7 @@ describe('ManageSpacePage', () => { color: '#aabbcc', initials: 'AB', disabledFeatures: [], + solution: 'es', }; const spacesManager = spacesManagerMock.create(); @@ -282,7 +295,9 @@ describe('ManageSpacePage', () => { catalogue: {}, spaces: { manage: true }, }} + eventTracker={eventTracker} allowFeatureVisibility + solutionNavExperiment={Promise.resolve(true)} /> ); @@ -299,7 +314,7 @@ describe('ManageSpacePage', () => { wrapper.update(); - updateSpace(wrapper); + updateSpace(wrapper, true, 'oblt'); await clickSaveButton(wrapper); @@ -311,6 +326,14 @@ describe('ManageSpacePage', () => { initials: 'AB', imageUrl: '', disabledFeatures: ['feature-1'], + solution: 'oblt', // solution has been changed + }); + + expect(reportEvent).toHaveBeenCalledWith('space_solution_changed', { + action: 'edit', + solution: 'oblt', + solution_prev: 'es', + space_id: 'existing-space', }); }); @@ -350,6 +373,7 @@ describe('ManageSpacePage', () => { catalogue: {}, spaces: { manage: true }, }} + eventTracker={eventTracker} allowFeatureVisibility /> ); @@ -399,6 +423,7 @@ describe('ManageSpacePage', () => { catalogue: {}, spaces: { manage: true }, }} + eventTracker={eventTracker} allowFeatureVisibility /> ); @@ -436,6 +461,7 @@ describe('ManageSpacePage', () => { catalogue: {}, spaces: { manage: true }, }} + eventTracker={eventTracker} allowFeatureVisibility /> ); @@ -497,6 +523,7 @@ describe('ManageSpacePage', () => { catalogue: {}, spaces: { manage: true }, }} + eventTracker={eventTracker} allowFeatureVisibility /> ); @@ -521,7 +548,11 @@ describe('ManageSpacePage', () => { }); }); -function updateSpace(wrapper: ReactWrapper, updateFeature = true) { +function updateSpace( + wrapper: ReactWrapper, + updateFeature = true, + solution?: SolutionView +) { const nameInput = wrapper.find('input[name="name"]'); const descriptionInput = wrapper.find('textarea[name="description"]'); @@ -531,6 +562,16 @@ function updateSpace(wrapper: ReactWrapper, updateFeature = true) { if (updateFeature) { toggleFeature(wrapper); } + + if (solution) { + act(() => { + findTestSubject(wrapper, `solutionViewSelect`).simulate('click'); + }); + wrapper.update(); + findTestSubject(wrapper, `solutionView${capitalizeFirstLetter(solution)}Option`).simulate( + 'click' + ); + } } function toggleFeature(wrapper: ReactWrapper) { @@ -552,3 +593,7 @@ async function clickSaveButton(wrapper: ReactWrapper) { wrapper.update(); } + +function capitalizeFirstLetter(string: string) { + return string.charAt(0).toUpperCase() + string.slice(1); +} diff --git a/x-pack/plugins/spaces/public/management/edit_space/manage_space_page.tsx b/x-pack/plugins/spaces/public/management/edit_space/manage_space_page.tsx index 7594778062857..7b11bf8a4609d 100644 --- a/x-pack/plugins/spaces/public/management/edit_space/manage_space_page.tsx +++ b/x-pack/plugins/spaces/public/management/edit_space/manage_space_page.tsx @@ -33,6 +33,7 @@ import { EnabledFeatures } from './enabled_features'; import { SolutionView } from './solution_view'; import type { Space } from '../../../common'; import { isReservedSpace } from '../../../common'; +import type { EventTracker } from '../../analytics'; import { getSpacesFeatureDescription } from '../../constants'; import { getSpaceColor, getSpaceInitials } from '../../space_avatar'; import type { SpacesManager } from '../../spaces_manager'; @@ -57,6 +58,7 @@ interface Props { history: ScopedHistory; allowFeatureVisibility: boolean; solutionNavExperiment?: Promise; + eventTracker: EventTracker; } interface State { @@ -454,14 +456,30 @@ export class ManageSpacePage extends Component { }; let action; - if (this.editingExistingSpace()) { - action = this.props.spacesManager.updateSpace(params); + const isEditing = this.editingExistingSpace(); + const { spacesManager, eventTracker } = this.props; + + if (isEditing) { + action = spacesManager.updateSpace(params); } else { - action = this.props.spacesManager.createSpace(params); + action = spacesManager.createSpace(params); } this.setState({ saveInProgress: true }); + const trackSpaceSolutionChange = () => { + const hasChangedSolution = this.state.originalSpace?.solution !== solution; + + if (!hasChangedSolution || solution === undefined) return; + + eventTracker.spaceSolutionChanged({ + spaceId: id, + solution, + solutionPrev: this.state.originalSpace?.solution, + action: isEditing ? 'edit' : 'create', + }); + }; + action .then(() => { this.props.notifications.toasts.addSuccess( @@ -474,11 +492,15 @@ export class ManageSpacePage extends Component { ) ); + trackSpaceSolutionChange(); this.backToSpacesList(); if (requireRefresh) { - setTimeout(() => { - window.location.reload(); + const flushAnalyticsEvents = window.__kbnAnalytics?.flush ?? (() => Promise.resolve()); + flushAnalyticsEvents().then(() => { + setTimeout(() => { + window.location.reload(); + }); }); } }) diff --git a/x-pack/plugins/spaces/public/management/management_service.test.ts b/x-pack/plugins/spaces/public/management/management_service.test.ts index 2eaacfda7c3a9..115c6a82ec0f7 100644 --- a/x-pack/plugins/spaces/public/management/management_service.test.ts +++ b/x-pack/plugins/spaces/public/management/management_service.test.ts @@ -12,10 +12,13 @@ import { managementPluginMock } from '@kbn/management-plugin/public/mocks'; import { ManagementService } from './management_service'; import { getRolesAPIClientMock } from './roles_api_client.mock'; +import { EventTracker } from '../analytics'; import type { ConfigType } from '../config'; import type { PluginsStart } from '../plugin'; import { spacesManagerMock } from '../spaces_manager/mocks'; +const eventTracker = new EventTracker({ reportEvent: jest.fn() }); + describe('ManagementService', () => { const config: ConfigType = { maxSpaces: 1000, @@ -39,6 +42,7 @@ describe('ManagementService', () => { config, getRolesAPIClient: getRolesAPIClientMock, solutionNavExperiment: Promise.resolve(false), + eventTracker, }); expect(mockKibanaSection.registerApp).toHaveBeenCalledTimes(1); @@ -60,6 +64,7 @@ describe('ManagementService', () => { config, getRolesAPIClient: getRolesAPIClientMock, solutionNavExperiment: Promise.resolve(false), + eventTracker, }); }); }); @@ -82,6 +87,7 @@ describe('ManagementService', () => { config, getRolesAPIClient: jest.fn(), solutionNavExperiment: Promise.resolve(false), + eventTracker, }); service.stop(); diff --git a/x-pack/plugins/spaces/public/management/management_service.tsx b/x-pack/plugins/spaces/public/management/management_service.tsx index 143aebba39d96..9975be6ac2986 100644 --- a/x-pack/plugins/spaces/public/management/management_service.tsx +++ b/x-pack/plugins/spaces/public/management/management_service.tsx @@ -10,6 +10,7 @@ import type { ManagementApp, ManagementSetup } from '@kbn/management-plugin/publ import type { RolesAPIClient } from '@kbn/security-plugin-types-public'; import { spacesManagementApp } from './spaces_management_app'; +import type { EventTracker } from '../analytics'; import type { ConfigType } from '../config'; import type { PluginsStart } from '../plugin'; import type { SpacesManager } from '../spaces_manager'; @@ -21,6 +22,7 @@ interface SetupDeps { config: ConfigType; getRolesAPIClient: () => Promise; solutionNavExperiment: Promise; + eventTracker: EventTracker; } export class ManagementService { @@ -33,6 +35,7 @@ export class ManagementService { config, getRolesAPIClient, solutionNavExperiment, + eventTracker, }: SetupDeps) { this.registeredSpacesManagementApp = management.sections.section.kibana.registerApp( spacesManagementApp.create({ @@ -41,6 +44,7 @@ export class ManagementService { config, getRolesAPIClient, solutionNavExperiment, + eventTracker, }) ); } diff --git a/x-pack/plugins/spaces/public/management/spaces_management_app.test.tsx b/x-pack/plugins/spaces/public/management/spaces_management_app.test.tsx index e953c324be285..065c5b080fcea 100644 --- a/x-pack/plugins/spaces/public/management/spaces_management_app.test.tsx +++ b/x-pack/plugins/spaces/public/management/spaces_management_app.test.tsx @@ -22,6 +22,7 @@ import { coreMock, scopedHistoryMock, themeServiceMock } from '@kbn/core/public/ import { featuresPluginMock } from '@kbn/features-plugin/public/mocks'; import { spacesManagementApp } from './spaces_management_app'; +import { EventTracker } from '../analytics'; import type { ConfigType } from '../config'; import type { PluginsStart } from '../plugin'; import { spacesManagerMock } from '../spaces_manager/mocks'; @@ -31,6 +32,8 @@ const config: ConfigType = { allowFeatureVisibility: true, }; +const eventTracker = new EventTracker({ reportEvent: jest.fn() }); + async function mountApp(basePath: string, pathname: string, spaceId?: string) { const container = document.createElement('div'); const setBreadcrumbs = jest.fn(); @@ -54,6 +57,7 @@ async function mountApp(basePath: string, pathname: string, spaceId?: string) { config, getRolesAPIClient: jest.fn(), solutionNavExperiment: Promise.resolve(false), + eventTracker, }) .mount({ basePath, @@ -76,6 +80,7 @@ describe('spacesManagementApp', () => { config, getRolesAPIClient: jest.fn(), solutionNavExperiment: Promise.resolve(false), + eventTracker, }) ).toMatchInlineSnapshot(` Object { @@ -127,7 +132,7 @@ describe('spacesManagementApp', () => { css="You have tried to stringify object returned from \`css\` function. It isn't supposed to be used directly (e.g. as value of the \`className\` prop), but rather handed to emotion so it can handle it (e.g. as value of \`css\` prop)." data-test-subj="kbnRedirectAppLink" > - Spaces Edit Page: {"capabilities":{"catalogue":{},"management":{},"navLinks":{}},"notifications":{"toasts":{}},"spacesManager":{"onActiveSpaceChange$":{}},"history":{"action":"PUSH","length":1,"location":{"pathname":"/create","search":"","hash":""}},"allowFeatureVisibility":true,"solutionNavExperiment":{}} + Spaces Edit Page: {"capabilities":{"catalogue":{},"management":{},"navLinks":{}},"notifications":{"toasts":{}},"spacesManager":{"onActiveSpaceChange$":{}},"history":{"action":"PUSH","length":1,"location":{"pathname":"/create","search":"","hash":""}},"allowFeatureVisibility":true,"solutionNavExperiment":{},"eventTracker":{"analytics":{}}} `); @@ -160,7 +165,7 @@ describe('spacesManagementApp', () => { css="You have tried to stringify object returned from \`css\` function. It isn't supposed to be used directly (e.g. as value of the \`className\` prop), but rather handed to emotion so it can handle it (e.g. as value of \`css\` prop)." data-test-subj="kbnRedirectAppLink" > - Spaces Edit Page: {"capabilities":{"catalogue":{},"management":{},"navLinks":{}},"notifications":{"toasts":{}},"spacesManager":{"onActiveSpaceChange$":{}},"spaceId":"some-space","history":{"action":"PUSH","length":1,"location":{"pathname":"/edit/some-space","search":"","hash":""}},"allowFeatureVisibility":true,"solutionNavExperiment":{}} + Spaces Edit Page: {"capabilities":{"catalogue":{},"management":{},"navLinks":{}},"notifications":{"toasts":{}},"spacesManager":{"onActiveSpaceChange$":{}},"spaceId":"some-space","history":{"action":"PUSH","length":1,"location":{"pathname":"/edit/some-space","search":"","hash":""}},"allowFeatureVisibility":true,"solutionNavExperiment":{},"eventTracker":{"analytics":{}}} `); diff --git a/x-pack/plugins/spaces/public/management/spaces_management_app.tsx b/x-pack/plugins/spaces/public/management/spaces_management_app.tsx index c551b47cde9c6..d08e90dd70024 100644 --- a/x-pack/plugins/spaces/public/management/spaces_management_app.tsx +++ b/x-pack/plugins/spaces/public/management/spaces_management_app.tsx @@ -19,6 +19,7 @@ import { RedirectAppLinks } from '@kbn/shared-ux-link-redirect-app'; import { Route, Router, Routes } from '@kbn/shared-ux-router'; import type { Space } from '../../common'; +import type { EventTracker } from '../analytics'; import type { ConfigType } from '../config'; import type { PluginsStart } from '../plugin'; import type { SpacesManager } from '../spaces_manager'; @@ -29,11 +30,18 @@ interface CreateParams { config: ConfigType; getRolesAPIClient: () => Promise; solutionNavExperiment: Promise; + eventTracker: EventTracker; } export const spacesManagementApp = Object.freeze({ id: 'spaces', - create({ getStartServices, spacesManager, config, solutionNavExperiment }: CreateParams) { + create({ + getStartServices, + spacesManager, + config, + solutionNavExperiment, + eventTracker, + }: CreateParams) { const title = i18n.translate('xpack.spaces.displayName', { defaultMessage: 'Spaces', }); @@ -90,6 +98,7 @@ export const spacesManagementApp = Object.freeze({ history={history} allowFeatureVisibility={config.allowFeatureVisibility} solutionNavExperiment={solutionNavExperiment} + eventTracker={eventTracker} /> ); }; @@ -117,6 +126,7 @@ export const spacesManagementApp = Object.freeze({ history={history} allowFeatureVisibility={config.allowFeatureVisibility} solutionNavExperiment={solutionNavExperiment} + eventTracker={eventTracker} /> ); }; diff --git a/x-pack/plugins/spaces/public/nav_control/components/spaces_menu.tsx b/x-pack/plugins/spaces/public/nav_control/components/spaces_menu.tsx index fab2158cb1c7d..610e3d82339c3 100644 --- a/x-pack/plugins/spaces/public/nav_control/components/spaces_menu.tsx +++ b/x-pack/plugins/spaces/public/nav_control/components/spaces_menu.tsx @@ -30,6 +30,7 @@ import { FormattedMessage, injectI18n } from '@kbn/i18n-react'; import { ManageSpacesButton } from './manage_spaces_button'; import type { Space } from '../../../common'; import { addSpaceIdToPath, ENTER_SPACE_PATH, SPACE_SEARCH_COUNT_THRESHOLD } from '../../../common'; +import type { EventTracker } from '../../analytics'; import { getSpaceAvatarComponent } from '../../space_avatar'; import { SpaceSolutionBadge } from '../../space_solution_badge'; @@ -48,6 +49,7 @@ interface Props { navigateToUrl: ApplicationStart['navigateToUrl']; readonly activeSpace: Space | null; isSolutionNavEnabled: boolean; + eventTracker: EventTracker; } class SpacesMenuUI extends Component { public render() { @@ -95,7 +97,7 @@ class SpacesMenuUI extends Component { id={this.props.id} className={'spcMenu'} title={i18n.translate('xpack.spaces.navControl.spacesMenu.changeCurrentSpaceTitle', { - defaultMessage: 'Change current space', + defaultMessage: 'Change current space xx', })} {...searchableProps} noMatchesMessage={noSpacesMessage} @@ -148,16 +150,36 @@ class SpacesMenuUI extends Component { }); }; - private spaceSelectionChange = ( + private getSpaceDetails = (id: string): Space | undefined => { + return this.props.spaces.find((space) => space.id === id); + }; + + private spaceSelectionChange = async ( newOptions: EuiSelectableOption[], event: EuiSelectableOnChangeEvent ) => { const selectedSpaceItem = newOptions.filter((item) => item.checked === 'on')[0]; + const trackSpaceChange = (nextId?: string) => { + if (!nextId) return; + const nextSpace = this.getSpaceDetails(nextId); + const currentSpace = this.props.activeSpace; + if (!nextSpace || !currentSpace) return; + + this.props.eventTracker.changeSpace({ + nextSpaceId: nextSpace.id, + nextSolution: nextSpace.solution, + prevSpaceId: currentSpace.id, + prevSolution: currentSpace.solution, + }); + }; + if (!!selectedSpaceItem) { + const spaceId = selectedSpaceItem.key; // the key is the unique space id + const urlToSelectedSpace = addSpaceIdToPath( this.props.serverBasePath, - selectedSpaceItem.key, // the key is the unique space id + spaceId, ENTER_SPACE_PATH ); @@ -169,15 +191,23 @@ class SpacesMenuUI extends Component { if (event.shiftKey) { // Open in new window, shift is given priority over other modifiers this.props.toggleSpaceSelector(); + trackSpaceChange(spaceId); window.open(urlToSelectedSpace); } else if (event.ctrlKey || event.metaKey || middleClick) { // Open in new tab - either a ctrl click or middle mouse button + trackSpaceChange(spaceId); window.open(urlToSelectedSpace, '_blank'); } else { // Force full page reload (usually not a good idea, but we need to in order to change spaces) // If the selected space is already the active space, gracefully close the popover - if (this.props.activeSpace?.id === selectedSpaceItem.key) this.props.toggleSpaceSelector(); - else this.props.navigateToUrl(urlToSelectedSpace); + if (this.props.activeSpace?.id === selectedSpaceItem.key) { + this.props.toggleSpaceSelector(); + } else { + trackSpaceChange(spaceId); + const flushAnalyticsEvents = window.__kbnAnalytics?.flush ?? (() => Promise.resolve()); + await flushAnalyticsEvents(); + this.props.navigateToUrl(urlToSelectedSpace); + } } } }; diff --git a/x-pack/plugins/spaces/public/nav_control/nav_control.tsx b/x-pack/plugins/spaces/public/nav_control/nav_control.tsx index 7e104e56c6548..b150ab7cbbbd0 100644 --- a/x-pack/plugins/spaces/public/nav_control/nav_control.tsx +++ b/x-pack/plugins/spaces/public/nav_control/nav_control.tsx @@ -12,12 +12,14 @@ import ReactDOM from 'react-dom'; import type { CoreStart } from '@kbn/core/public'; import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render'; +import type { EventTracker } from '../analytics'; import type { SpacesManager } from '../spaces_manager'; export function initSpacesNavControl( spacesManager: SpacesManager, core: CoreStart, - solutionNavExperiment: Promise + solutionNavExperiment: Promise, + eventTracker: EventTracker ) { core.chrome.navControls.registerLeft({ order: 1000, @@ -43,6 +45,7 @@ export function initSpacesNavControl( navigateToApp={core.application.navigateToApp} navigateToUrl={core.application.navigateToUrl} solutionNavExperiment={solutionNavExperiment} + eventTracker={eventTracker} /> , diff --git a/x-pack/plugins/spaces/public/nav_control/nav_control_popover.test.tsx b/x-pack/plugins/spaces/public/nav_control/nav_control_popover.test.tsx index 528103c5ccbc2..e402217e3db36 100644 --- a/x-pack/plugins/spaces/public/nav_control/nav_control_popover.test.tsx +++ b/x-pack/plugins/spaces/public/nav_control/nav_control_popover.test.tsx @@ -16,10 +16,11 @@ import { act, render, waitFor } from '@testing-library/react'; import React from 'react'; import * as Rx from 'rxjs'; -import { mountWithIntl } from '@kbn/test-jest-helpers'; +import { findTestSubject, mountWithIntl } from '@kbn/test-jest-helpers'; import { NavControlPopover } from './nav_control_popover'; import type { Space } from '../../common'; +import { EventTracker } from '../analytics'; import { SpaceAvatarInternal } from '../space_avatar/space_avatar_internal'; import { SpaceSolutionBadge } from '../space_solution_badge'; import type { SpacesManager } from '../spaces_manager'; @@ -44,11 +45,19 @@ const mockSpaces = [ }, ]; +const reportEvent = jest.fn(); +const eventTracker = new EventTracker({ reportEvent }); + describe('NavControlPopover', () => { - async function setup(spaces: Space[], isSolutionNavEnabled = false) { + async function setup(spaces: Space[], isSolutionNavEnabled = false, activeSpace?: Space) { const spacesManager = spacesManagerMock.create(); spacesManager.getSpaces = jest.fn().mockResolvedValue(spaces); + if (activeSpace) { + // @ts-ignore readonly check + spacesManager.onActiveSpaceChange$ = Rx.of(activeSpace); + } + const wrapper = mountWithIntl( { navigateToApp={jest.fn()} navigateToUrl={jest.fn()} solutionNavExperiment={Promise.resolve(isSolutionNavEnabled)} + eventTracker={eventTracker} /> ); @@ -80,6 +90,7 @@ describe('NavControlPopover', () => { navigateToApp={jest.fn()} navigateToUrl={jest.fn()} solutionNavExperiment={Promise.resolve(false)} + eventTracker={eventTracker} /> ); expect(baseElement).toMatchSnapshot(); @@ -105,6 +116,7 @@ describe('NavControlPopover', () => { navigateToApp={jest.fn()} navigateToUrl={jest.fn()} solutionNavExperiment={Promise.resolve(false)} + eventTracker={eventTracker} /> ); @@ -253,4 +265,40 @@ describe('NavControlPopover', () => { expect(wrapper.find(SpaceSolutionBadge)).toHaveLength(2); }); + + it('should report event when switching space', async () => { + const spaces: Space[] = [ + { + id: 'space-1', + name: 'Space-1', + disabledFeatures: [], + solution: 'classic', + }, + { + id: 'space-2', + name: 'Space 2', + disabledFeatures: [], + solution: 'security', + }, + ]; + + const activeSpace = spaces[0]; + const wrapper = await setup(spaces, true /** isSolutionEnabled **/, activeSpace); + + await act(async () => { + wrapper.find(EuiHeaderSectionItemButton).find('button').simulate('click'); + }); + wrapper.update(); + + expect(reportEvent).not.toHaveBeenCalled(); + + findTestSubject(wrapper, 'space-2-selectableSpaceItem').simulate('click'); + + expect(reportEvent).toHaveBeenCalledWith('space_changed', { + solution: 'security', + solution_prev: 'classic', + space_id: 'space-2', + space_id_prev: 'space-1', + }); + }); }); diff --git a/x-pack/plugins/spaces/public/nav_control/nav_control_popover.tsx b/x-pack/plugins/spaces/public/nav_control/nav_control_popover.tsx index 6f40db60bfd4f..e79e2750bb5bd 100644 --- a/x-pack/plugins/spaces/public/nav_control/nav_control_popover.tsx +++ b/x-pack/plugins/spaces/public/nav_control/nav_control_popover.tsx @@ -21,6 +21,7 @@ import { i18n } from '@kbn/i18n'; import { SpacesDescription } from './components/spaces_description'; import { SpacesMenu } from './components/spaces_menu'; import type { Space } from '../../common'; +import type { EventTracker } from '../analytics'; import { getSpaceAvatarComponent } from '../space_avatar'; import type { SpacesManager } from '../spaces_manager'; @@ -38,6 +39,7 @@ interface Props { serverBasePath: string; theme: WithEuiThemeProps['theme']; solutionNavExperiment: Promise; + eventTracker: EventTracker; } interface State { @@ -109,6 +111,7 @@ class NavControlPopoverUI extends Component { navigateToUrl={this.props.navigateToUrl} activeSpace={this.state.activeSpace} isSolutionNavEnabled={this.state.isSolutionNavEnabled} + eventTracker={this.props.eventTracker} /> ); } diff --git a/x-pack/plugins/spaces/public/plugin.tsx b/x-pack/plugins/spaces/public/plugin.tsx index c915a4ea73880..13f3364d54e94 100644 --- a/x-pack/plugins/spaces/public/plugin.tsx +++ b/x-pack/plugins/spaces/public/plugin.tsx @@ -13,6 +13,7 @@ import type { HomePublicPluginSetup } from '@kbn/home-plugin/public'; import type { ManagementSetup, ManagementStart } from '@kbn/management-plugin/public'; import type { SecurityPluginStart } from '@kbn/security-plugin-types-public'; +import { EventTracker, registerAnalyticsContext, registerSpacesEventTypes } from './analytics'; import type { ConfigType } from './config'; import { createSpacesFeatureCatalogueEntry } from './create_feature_catalogue_entry'; import { isSolutionNavEnabled } from './experiments'; @@ -49,6 +50,7 @@ export type SpacesPluginStart = ReturnType; export class SpacesPlugin implements Plugin { private spacesManager!: SpacesManager; private spacesApi!: SpacesApi; + private eventTracker!: EventTracker; private managementService?: ManagementService; private readonly config: ConfigType; @@ -73,6 +75,9 @@ export class SpacesPlugin implements Plugin isSolutionNavEnabled(cloud, cloudExperiments)) @@ -109,6 +114,7 @@ export class SpacesPlugin implements Plugin feature.id); - const knownSolutions = ['classic', 'es', 'oblt', 'security', 'unset']; + const knownSolutions: Array = [ + 'classic', + 'es', + 'oblt', + 'security', + 'unset', + ]; const resp = (await esClient.search({ index: kibanaIndex, diff --git a/x-pack/plugins/spaces/tsconfig.json b/x-pack/plugins/spaces/tsconfig.json index 9f79252873307..e06e3bec498de 100644 --- a/x-pack/plugins/spaces/tsconfig.json +++ b/x-pack/plugins/spaces/tsconfig.json @@ -38,6 +38,7 @@ "@kbn/security-plugin-types-public", "@kbn/cloud-plugin", "@kbn/cloud-experiments-plugin", + "@kbn/core-analytics-browser" ], "exclude": [ "target/**/*", From 687df5188f20650e9197f66fcd316124e4420626 Mon Sep 17 00:00:00 2001 From: Davis Plumlee <56367316+dplumlee@users.noreply.github.com> Date: Thu, 1 Aug 2024 10:15:06 -0400 Subject: [PATCH 4/8] [Security Solution] Adds diff algorithm and unit tests for `data_source` field (#188874) ## Summary Related ticket: https://github.com/elastic/kibana/issues/187659 Adds the diff algorithm and unit test coverage for the `data_source` field we use in the prebuilt rules customization workflow. This field is a custom grouped field that combines the `data_view_id` field and `index_pattern` field that are used interchangeably of one another on the rule type for a rule's data source. ### Checklist Delete any items that are not applicable to this PR. - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios ### For maintainers - [ ] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) --- .../three_way_diff/three_way_diff_outcome.ts | 34 ++ .../data_source_diff_algorithm.test.ts | 428 ++++++++++++++++++ .../algorithms/data_source_diff_algorithm.ts | 148 ++++++ .../diff/calculation/algorithms/helpers.ts | 37 ++ .../diff/calculation/algorithms/index.ts | 1 + .../algorithms/scalar_array_diff_algorithm.ts | 18 +- 6 files changed, 655 insertions(+), 11 deletions(-) create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/data_source_diff_algorithm.test.ts create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/data_source_diff_algorithm.ts create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/helpers.ts diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/model/diff/three_way_diff/three_way_diff_outcome.ts b/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/model/diff/three_way_diff/three_way_diff_outcome.ts index 44885d1932070..d5d656f3bb385 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/model/diff/three_way_diff/three_way_diff_outcome.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/model/diff/three_way_diff/three_way_diff_outcome.ts @@ -7,6 +7,8 @@ import { isEqual } from 'lodash'; import { MissingVersion } from './three_way_diff'; +import type { RuleDataSource } from '../diffable_rule/diffable_field_types'; +import { DataSourceType } from '../diffable_rule/diffable_field_types'; /** * Result of comparing three versions of a value against each other. @@ -75,6 +77,33 @@ export const determineOrderAgnosticDiffOutcome = ( }); }; +/** + * Determines diff outcome for `data_source` field + * + * NOTE: uses order agnostic comparison for nested array fields (e.g. `index`) + */ +export const determineDiffOutcomeForDataSource = ( + baseVersion: RuleDataSource | MissingVersion, + currentVersion: RuleDataSource, + targetVersion: RuleDataSource +): ThreeWayDiffOutcome => { + const isBaseVersionMissing = baseVersion === MissingVersion; + + if ( + (isBaseVersionMissing || isIndexPatternDataSourceType(baseVersion)) && + isIndexPatternDataSourceType(currentVersion) && + isIndexPatternDataSourceType(targetVersion) + ) { + return determineOrderAgnosticDiffOutcome( + isBaseVersionMissing ? MissingVersion : baseVersion.index_patterns, + currentVersion.index_patterns, + targetVersion.index_patterns + ); + } + + return determineDiffOutcome(baseVersion, currentVersion, targetVersion); +}; + interface DetermineDiffOutcomeProps { baseEqlCurrent: boolean; baseEqlTarget: boolean; @@ -121,3 +150,8 @@ export const determineIfValueCanUpdate = (diffCase: ThreeWayDiffOutcome): boolea diffCase === ThreeWayDiffOutcome.MissingBaseCanUpdate ); }; + +const isIndexPatternDataSourceType = ( + version: RuleDataSource +): version is Extract => + version.type === DataSourceType.index_patterns; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/data_source_diff_algorithm.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/data_source_diff_algorithm.test.ts new file mode 100644 index 0000000000000..6944cd137448a --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/data_source_diff_algorithm.test.ts @@ -0,0 +1,428 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { + RuleDataSource, + ThreeVersionsOf, +} from '../../../../../../../../common/api/detection_engine'; +import { + ThreeWayDiffOutcome, + ThreeWayMergeOutcome, + MissingVersion, + DataSourceType, + ThreeWayDiffConflict, +} from '../../../../../../../../common/api/detection_engine'; +import { dataSourceDiffAlgorithm } from './data_source_diff_algorithm'; + +describe('dataSourceDiffAlgorithm', () => { + describe('returns current_version as merged output if there is no update - scenario AAA', () => { + it('if all versions are index patterns', () => { + const mockVersions: ThreeVersionsOf = { + base_version: { + type: DataSourceType.index_patterns, + index_patterns: ['one', 'two', 'two', 'three'], + }, + current_version: { + type: DataSourceType.index_patterns, + index_patterns: ['one', 'three', 'two'], + }, + target_version: { + type: DataSourceType.index_patterns, + index_patterns: ['one', 'two', 'three'], + }, + }; + + const result = dataSourceDiffAlgorithm(mockVersions); + + expect(result).toEqual( + expect.objectContaining({ + merged_version: mockVersions.current_version, + diff_outcome: ThreeWayDiffOutcome.StockValueNoUpdate, + merge_outcome: ThreeWayMergeOutcome.Current, + conflict: ThreeWayDiffConflict.NONE, + }) + ); + }); + + it('if all versions are data views', () => { + const mockVersions: ThreeVersionsOf = { + base_version: { type: DataSourceType.data_view, data_view_id: '123' }, + current_version: { type: DataSourceType.data_view, data_view_id: '123' }, + target_version: { type: DataSourceType.data_view, data_view_id: '123' }, + }; + + const result = dataSourceDiffAlgorithm(mockVersions); + + expect(result).toEqual( + expect.objectContaining({ + merged_version: mockVersions.current_version, + diff_outcome: ThreeWayDiffOutcome.StockValueNoUpdate, + merge_outcome: ThreeWayMergeOutcome.Current, + conflict: ThreeWayDiffConflict.NONE, + }) + ); + }); + }); + + describe('returns current_version as merged output if current_version is different and there is no update - scenario ABA', () => { + it('if current version is different data type than base and target', () => { + const mockVersions: ThreeVersionsOf = { + base_version: { + type: DataSourceType.index_patterns, + index_patterns: ['one', 'two', 'three'], + }, + current_version: { type: DataSourceType.data_view, data_view_id: '123' }, + target_version: { + type: DataSourceType.index_patterns, + index_patterns: ['one', 'two', 'three'], + }, + }; + + const result = dataSourceDiffAlgorithm(mockVersions); + + expect(result).toEqual( + expect.objectContaining({ + merged_version: mockVersions.current_version, + diff_outcome: ThreeWayDiffOutcome.CustomizedValueNoUpdate, + merge_outcome: ThreeWayMergeOutcome.Current, + conflict: ThreeWayDiffConflict.NONE, + }) + ); + }); + + it('if all versions are same data type', () => { + const mockVersions: ThreeVersionsOf = { + base_version: { + type: DataSourceType.index_patterns, + index_patterns: ['one', 'two', 'three'], + }, + current_version: { + type: DataSourceType.index_patterns, + index_patterns: ['one', 'three', 'four'], + }, + target_version: { + type: DataSourceType.index_patterns, + index_patterns: ['one', 'two', 'three'], + }, + }; + + const result = dataSourceDiffAlgorithm(mockVersions); + + expect(result).toEqual( + expect.objectContaining({ + merged_version: mockVersions.current_version, + diff_outcome: ThreeWayDiffOutcome.CustomizedValueNoUpdate, + merge_outcome: ThreeWayMergeOutcome.Current, + conflict: ThreeWayDiffConflict.NONE, + }) + ); + }); + }); + + describe('returns target_version as merged output if current_version is the same and there is an update - scenario AAB', () => { + it('if target version is different data type than base and current', () => { + const mockVersions: ThreeVersionsOf = { + base_version: { type: DataSourceType.data_view, data_view_id: '123' }, + current_version: { type: DataSourceType.data_view, data_view_id: '123' }, + target_version: { + type: DataSourceType.index_patterns, + index_patterns: ['one', 'two', 'three'], + }, + }; + + const result = dataSourceDiffAlgorithm(mockVersions); + + expect(result).toEqual( + expect.objectContaining({ + merged_version: mockVersions.target_version, + diff_outcome: ThreeWayDiffOutcome.StockValueCanUpdate, + merge_outcome: ThreeWayMergeOutcome.Target, + conflict: ThreeWayDiffConflict.NONE, + }) + ); + }); + + it('if all versions are same data type', () => { + const mockVersions: ThreeVersionsOf = { + base_version: { type: DataSourceType.data_view, data_view_id: '123' }, + current_version: { type: DataSourceType.data_view, data_view_id: '123' }, + target_version: { type: DataSourceType.data_view, data_view_id: '456' }, + }; + + const result = dataSourceDiffAlgorithm(mockVersions); + + expect(result).toEqual( + expect.objectContaining({ + merged_version: mockVersions.target_version, + diff_outcome: ThreeWayDiffOutcome.StockValueCanUpdate, + merge_outcome: ThreeWayMergeOutcome.Target, + conflict: ThreeWayDiffConflict.NONE, + }) + ); + }); + }); + + describe('returns current_version as merged output if current version is different but it matches the update - scenario ABB', () => { + it('if all versions are index patterns', () => { + const mockVersions: ThreeVersionsOf = { + base_version: { + type: DataSourceType.index_patterns, + index_patterns: ['one', 'two', 'three'], + }, + current_version: { + type: DataSourceType.index_patterns, + index_patterns: ['one', 'three', 'four'], + }, + target_version: { + type: DataSourceType.index_patterns, + index_patterns: ['one', 'three', 'four'], + }, + }; + + const result = dataSourceDiffAlgorithm(mockVersions); + + expect(result).toEqual( + expect.objectContaining({ + merged_version: mockVersions.current_version, + diff_outcome: ThreeWayDiffOutcome.CustomizedValueSameUpdate, + merge_outcome: ThreeWayMergeOutcome.Current, + conflict: ThreeWayDiffConflict.NONE, + }) + ); + }); + + it('if all versions are data views', () => { + const mockVersions: ThreeVersionsOf = { + base_version: { type: DataSourceType.data_view, data_view_id: '123' }, + current_version: { type: DataSourceType.data_view, data_view_id: '456' }, + target_version: { type: DataSourceType.data_view, data_view_id: '456' }, + }; + + const result = dataSourceDiffAlgorithm(mockVersions); + + expect(result).toEqual( + expect.objectContaining({ + merged_version: mockVersions.current_version, + diff_outcome: ThreeWayDiffOutcome.CustomizedValueSameUpdate, + merge_outcome: ThreeWayMergeOutcome.Current, + conflict: ThreeWayDiffConflict.NONE, + }) + ); + }); + }); + + describe('returns current_version as merged output if all three versions are different - scenario ABC', () => { + it('if all versions are index patterns', () => { + const mockVersions: ThreeVersionsOf = { + base_version: { + type: DataSourceType.index_patterns, + index_patterns: ['one', 'two', 'three'], + }, + current_version: { + type: DataSourceType.index_patterns, + index_patterns: ['one', 'three', 'four'], + }, + target_version: { + type: DataSourceType.index_patterns, + index_patterns: ['one', 'two', 'five'], + }, + }; + + const expectedMergedVersion: RuleDataSource = { + type: DataSourceType.index_patterns, + index_patterns: ['one', 'four', 'five'], + }; + + const result = dataSourceDiffAlgorithm(mockVersions); + + expect(result).toEqual( + expect.objectContaining({ + merged_version: expectedMergedVersion, + diff_outcome: ThreeWayDiffOutcome.CustomizedValueCanUpdate, + merge_outcome: ThreeWayMergeOutcome.Merged, + conflict: ThreeWayDiffConflict.SOLVABLE, + }) + ); + }); + + it('if all versions are data views', () => { + const mockVersions: ThreeVersionsOf = { + base_version: { type: DataSourceType.data_view, data_view_id: '123' }, + current_version: { type: DataSourceType.data_view, data_view_id: '456' }, + target_version: { type: DataSourceType.data_view, data_view_id: '789' }, + }; + + const result = dataSourceDiffAlgorithm(mockVersions); + + expect(result).toEqual( + expect.objectContaining({ + merged_version: mockVersions.current_version, + diff_outcome: ThreeWayDiffOutcome.CustomizedValueCanUpdate, + merge_outcome: ThreeWayMergeOutcome.Current, + conflict: ThreeWayDiffConflict.NON_SOLVABLE, + }) + ); + }); + + it('if base version is a data view and others are index patterns ', () => { + const mockVersions: ThreeVersionsOf = { + base_version: { type: DataSourceType.data_view, data_view_id: '123' }, + current_version: { + type: DataSourceType.index_patterns, + index_patterns: ['one', 'three', 'four'], + }, + target_version: { + type: DataSourceType.index_patterns, + index_patterns: ['one', 'two', 'five'], + }, + }; + + const expectedMergedVersion: RuleDataSource = { + type: DataSourceType.index_patterns, + index_patterns: ['one', 'three', 'four', 'two', 'five'], + }; + + const result = dataSourceDiffAlgorithm(mockVersions); + + expect(result).toEqual( + expect.objectContaining({ + merged_version: expectedMergedVersion, + diff_outcome: ThreeWayDiffOutcome.CustomizedValueCanUpdate, + merge_outcome: ThreeWayMergeOutcome.Merged, + conflict: ThreeWayDiffConflict.SOLVABLE, + }) + ); + }); + + it('if base version is a index patterns and other are data views', () => { + const mockVersions: ThreeVersionsOf = { + base_version: { + type: DataSourceType.index_patterns, + index_patterns: ['one', 'three', 'four'], + }, + current_version: { type: DataSourceType.data_view, data_view_id: '123' }, + target_version: { type: DataSourceType.data_view, data_view_id: '456' }, + }; + + const expectedMergedVersion: RuleDataSource = { + type: DataSourceType.data_view, + data_view_id: '123', + }; + + const result = dataSourceDiffAlgorithm(mockVersions); + + expect(result).toEqual( + expect.objectContaining({ + merged_version: expectedMergedVersion, + diff_outcome: ThreeWayDiffOutcome.CustomizedValueCanUpdate, + merge_outcome: ThreeWayMergeOutcome.Current, + conflict: ThreeWayDiffConflict.NON_SOLVABLE, + }) + ); + }); + + it('if currrent version is a different data type', () => { + const mockVersions: ThreeVersionsOf = { + base_version: { type: DataSourceType.data_view, data_view_id: '123' }, + current_version: { + type: DataSourceType.index_patterns, + index_patterns: ['one', 'three', 'four'], + }, + target_version: { type: DataSourceType.data_view, data_view_id: '789' }, + }; + + const result = dataSourceDiffAlgorithm(mockVersions); + + expect(result).toEqual( + expect.objectContaining({ + merged_version: mockVersions.current_version, + diff_outcome: ThreeWayDiffOutcome.CustomizedValueCanUpdate, + merge_outcome: ThreeWayMergeOutcome.Current, + conflict: ThreeWayDiffConflict.NON_SOLVABLE, + }) + ); + }); + + it('if target version is a different data type', () => { + const mockVersions: ThreeVersionsOf = { + base_version: { + type: DataSourceType.index_patterns, + index_patterns: ['one', 'two', 'three'], + }, + current_version: { + type: DataSourceType.index_patterns, + index_patterns: ['one', 'three', 'four'], + }, + target_version: { type: DataSourceType.data_view, data_view_id: '789' }, + }; + + const result = dataSourceDiffAlgorithm(mockVersions); + + expect(result).toEqual( + expect.objectContaining({ + merged_version: mockVersions.current_version, + diff_outcome: ThreeWayDiffOutcome.CustomizedValueCanUpdate, + merge_outcome: ThreeWayMergeOutcome.Current, + conflict: ThreeWayDiffConflict.NON_SOLVABLE, + }) + ); + }); + }); + + describe('if base_version is missing', () => { + it('returns current_version as merged output if current_version and target_version are the same - scenario -AA', () => { + const mockVersions: ThreeVersionsOf = { + base_version: MissingVersion, + current_version: { + type: DataSourceType.index_patterns, + index_patterns: ['one', 'three', 'four'], + }, + target_version: { + type: DataSourceType.index_patterns, + index_patterns: ['one', 'three', 'four'], + }, + }; + + const result = dataSourceDiffAlgorithm(mockVersions); + + expect(result).toEqual( + expect.objectContaining({ + has_base_version: false, + base_version: undefined, + merged_version: mockVersions.current_version, + diff_outcome: ThreeWayDiffOutcome.MissingBaseNoUpdate, + merge_outcome: ThreeWayMergeOutcome.Current, + conflict: ThreeWayDiffConflict.NONE, + }) + ); + }); + + it('returns target_version as merged output if current_version and target_version are different - scenario -AB', () => { + const mockVersions: ThreeVersionsOf = { + base_version: MissingVersion, + current_version: { type: DataSourceType.data_view, data_view_id: '456' }, + target_version: { + type: DataSourceType.index_patterns, + index_patterns: ['one', 'three', 'four'], + }, + }; + + const result = dataSourceDiffAlgorithm(mockVersions); + + expect(result).toEqual( + expect.objectContaining({ + has_base_version: false, + base_version: undefined, + merged_version: mockVersions.target_version, + diff_outcome: ThreeWayDiffOutcome.MissingBaseCanUpdate, + merge_outcome: ThreeWayMergeOutcome.Target, + conflict: ThreeWayDiffConflict.SOLVABLE, + }) + ); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/data_source_diff_algorithm.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/data_source_diff_algorithm.ts new file mode 100644 index 0000000000000..2f7430ddd4718 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/data_source_diff_algorithm.ts @@ -0,0 +1,148 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { assertUnreachable } from '../../../../../../../../common/utility_types'; +import type { + RuleDataSource, + ThreeVersionsOf, + ThreeWayDiff, +} from '../../../../../../../../common/api/detection_engine/prebuilt_rules'; +import { + determineIfValueCanUpdate, + ThreeWayDiffOutcome, + ThreeWayMergeOutcome, + MissingVersion, + DataSourceType, + ThreeWayDiffConflict, + determineDiffOutcomeForDataSource, +} from '../../../../../../../../common/api/detection_engine/prebuilt_rules'; +import { getDedupedDataSourceVersion, mergeDedupedArrays } from './helpers'; + +export const dataSourceDiffAlgorithm = ( + versions: ThreeVersionsOf +): ThreeWayDiff => { + const { + base_version: baseVersion, + current_version: currentVersion, + target_version: targetVersion, + } = versions; + + const diffOutcome = determineDiffOutcomeForDataSource(baseVersion, currentVersion, targetVersion); + + const valueCanUpdate = determineIfValueCanUpdate(diffOutcome); + + const hasBaseVersion = baseVersion !== MissingVersion; + + const { mergeOutcome, conflict, mergedVersion } = mergeVersions({ + baseVersion: hasBaseVersion ? baseVersion : undefined, + currentVersion, + targetVersion, + diffOutcome, + }); + + return { + has_base_version: hasBaseVersion, + base_version: hasBaseVersion ? baseVersion : undefined, + current_version: currentVersion, + target_version: targetVersion, + merged_version: mergedVersion, + merge_outcome: mergeOutcome, + + diff_outcome: diffOutcome, + conflict, + has_update: valueCanUpdate, + }; +}; + +interface MergeResult { + mergeOutcome: ThreeWayMergeOutcome; + mergedVersion: RuleDataSource; + conflict: ThreeWayDiffConflict; +} + +interface MergeArgs { + baseVersion: RuleDataSource | undefined; + currentVersion: RuleDataSource; + targetVersion: RuleDataSource; + diffOutcome: ThreeWayDiffOutcome; +} + +const mergeVersions = ({ + baseVersion, + currentVersion, + targetVersion, + diffOutcome, +}: MergeArgs): MergeResult => { + const dedupedBaseVersion = baseVersion ? getDedupedDataSourceVersion(baseVersion) : baseVersion; + const dedupedCurrentVersion = getDedupedDataSourceVersion(currentVersion); + const dedupedTargetVersion = getDedupedDataSourceVersion(targetVersion); + + switch (diffOutcome) { + // Scenario -AA is treated as scenario AAA: + // https://github.com/elastic/kibana/pull/184889#discussion_r1636421293 + case ThreeWayDiffOutcome.MissingBaseNoUpdate: + case ThreeWayDiffOutcome.StockValueNoUpdate: + case ThreeWayDiffOutcome.CustomizedValueNoUpdate: + case ThreeWayDiffOutcome.CustomizedValueSameUpdate: + return { + conflict: ThreeWayDiffConflict.NONE, + mergeOutcome: ThreeWayMergeOutcome.Current, + mergedVersion: dedupedCurrentVersion, + }; + + case ThreeWayDiffOutcome.StockValueCanUpdate: + return { + conflict: ThreeWayDiffConflict.NONE, + mergeOutcome: ThreeWayMergeOutcome.Target, + mergedVersion: dedupedTargetVersion, + }; + + case ThreeWayDiffOutcome.CustomizedValueCanUpdate: { + if ( + dedupedCurrentVersion.type === DataSourceType.index_patterns && + dedupedTargetVersion.type === DataSourceType.index_patterns + ) { + const baseVersionToMerge = + dedupedBaseVersion && dedupedBaseVersion.type === DataSourceType.index_patterns + ? dedupedBaseVersion.index_patterns + : []; + + return { + conflict: ThreeWayDiffConflict.SOLVABLE, + mergeOutcome: ThreeWayMergeOutcome.Merged, + mergedVersion: { + type: DataSourceType.index_patterns, + index_patterns: mergeDedupedArrays( + baseVersionToMerge, + dedupedCurrentVersion.index_patterns, + dedupedTargetVersion.index_patterns + ), + }, + }; + } + + return { + conflict: ThreeWayDiffConflict.NON_SOLVABLE, + mergeOutcome: ThreeWayMergeOutcome.Current, + mergedVersion: dedupedCurrentVersion, + }; + } + + // Scenario -AB is treated as scenario ABC, but marked as + // SOLVABLE, and returns the target version as the merged version + // https://github.com/elastic/kibana/pull/184889#discussion_r1636421293 + case ThreeWayDiffOutcome.MissingBaseCanUpdate: { + return { + mergedVersion: targetVersion, + mergeOutcome: ThreeWayMergeOutcome.Target, + conflict: ThreeWayDiffConflict.SOLVABLE, + }; + } + default: + return assertUnreachable(diffOutcome); + } +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/helpers.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/helpers.ts new file mode 100644 index 0000000000000..498b857eb0428 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/helpers.ts @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { difference, union, uniq } from 'lodash'; +import type { RuleDataSource } from '../../../../../../../../common/api/detection_engine'; +import { DataSourceType } from '../../../../../../../../common/api/detection_engine'; + +export const mergeDedupedArrays = ( + dedupedBaseVersion: T[], + dedupedCurrentVersion: T[], + dedupedTargetVersion: T[] +) => { + const addedCurrent = difference(dedupedCurrentVersion, dedupedBaseVersion); + const removedCurrent = difference(dedupedBaseVersion, dedupedCurrentVersion); + + const addedTarget = difference(dedupedTargetVersion, dedupedBaseVersion); + const removedTarget = difference(dedupedBaseVersion, dedupedTargetVersion); + + const bothAdded = union(addedCurrent, addedTarget); + const bothRemoved = union(removedCurrent, removedTarget); + + return difference(union(dedupedBaseVersion, bothAdded), bothRemoved); +}; + +export const getDedupedDataSourceVersion = (version: RuleDataSource): RuleDataSource => { + if (version.type === DataSourceType.index_patterns) { + return { + ...version, + index_patterns: uniq(version.index_patterns), + }; + } + return version; +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/index.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/index.ts index b7c0a1143f1a7..fc895543e66b2 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/index.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/index.ts @@ -10,3 +10,4 @@ export { singleLineStringDiffAlgorithm } from './single_line_string_diff_algorit export { scalarArrayDiffAlgorithm } from './scalar_array_diff_algorithm'; export { simpleDiffAlgorithm } from './simple_diff_algorithm'; export { multiLineStringDiffAlgorithm } from './multi_line_string_diff_algorithm'; +export { dataSourceDiffAlgorithm } from './data_source_diff_algorithm'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/scalar_array_diff_algorithm.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/scalar_array_diff_algorithm.ts index e990ad9aa7c33..215e92377a596 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/scalar_array_diff_algorithm.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/calculation/algorithms/scalar_array_diff_algorithm.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { difference, union, uniq } from 'lodash'; +import { uniq } from 'lodash'; import { assertUnreachable } from '../../../../../../../../common/utility_types'; import type { ThreeVersionsOf, @@ -19,6 +19,7 @@ import { ThreeWayDiffConflict, ThreeWayMergeOutcome, } from '../../../../../../../../common/api/detection_engine/prebuilt_rules'; +import { mergeDedupedArrays } from './helpers'; /** * Diff algorithm used for arrays of scalar values (eg. numbers, strings, booleans, etc.) @@ -105,16 +106,11 @@ const mergeVersions = ({ } case ThreeWayDiffOutcome.CustomizedValueCanUpdate: { - const addedCurrent = difference(dedupedCurrentVersion, dedupedBaseVersion); - const removedCurrent = difference(dedupedBaseVersion, dedupedCurrentVersion); - - const addedTarget = difference(dedupedTargetVersion, dedupedBaseVersion); - const removedTarget = difference(dedupedBaseVersion, dedupedTargetVersion); - - const bothAdded = union(addedCurrent, addedTarget); - const bothRemoved = union(removedCurrent, removedTarget); - - const merged = difference(union(dedupedBaseVersion, bothAdded), bothRemoved); + const merged = mergeDedupedArrays( + dedupedBaseVersion, + dedupedCurrentVersion, + dedupedTargetVersion + ); return { conflict: ThreeWayDiffConflict.SOLVABLE, From 1a583ac3dc260a1751b3e3188327a44c1c102a74 Mon Sep 17 00:00:00 2001 From: Saarika Bhasi <55930906+saarikabhasi@users.noreply.github.com> Date: Thu, 1 Aug 2024 10:24:33 -0400 Subject: [PATCH 5/8] [Index management] Unskip api_integration tests for inference endpoints (#189664) ## Summary Recently Elasticsearch GET all inference endpoints api response format was changed in [PR](https://github.com/elastic/elasticsearch/pull/111366) causing the api integration tests to fail. These tests were skipped in https://github.com/elastic/kibana/issues/189333#issuecomment-2254814105 and https://github.com/elastic/kibana/pull/189466 The [frontend route changes](https://github.com/elastic/kibana/pull/189545) required for these api integration tests to succeed has been merged and is [backported to 8.15](https://github.com/elastic/kibana/pull/189642). In this PR, we are un skipping these tests. ### Test reports Tested this changes serverless changes locally using qaf tool against cloud deployment and ran FTR tests for both serverless & stateful version. ### Checklist Delete any items that are not applicable to this PR. - [x] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed --- .../apis/management/index_management/inference_endpoints.ts | 6 ++---- .../common/index_management/inference_endpoints.ts | 5 ++--- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/x-pack/test/api_integration/apis/management/index_management/inference_endpoints.ts b/x-pack/test/api_integration/apis/management/index_management/inference_endpoints.ts index 40dd20104d06e..586c546414850 100644 --- a/x-pack/test/api_integration/apis/management/index_management/inference_endpoints.ts +++ b/x-pack/test/api_integration/apis/management/index_management/inference_endpoints.ts @@ -20,9 +20,7 @@ export default function ({ getService }: FtrProviderContext) { const service = 'elser'; const modelId = '.elser_model_2'; - // FLAKY: https://github.com/elastic/kibana/issues/189333 - // Failing: See https://github.com/elastic/kibana/issues/189333 - describe.skip('Inference endpoints', function () { + describe('Inference endpoints', function () { after(async () => { try { log.debug(`Deleting underlying trained model`); @@ -56,7 +54,7 @@ export default function ({ getService }: FtrProviderContext) { inferenceEndpoints.some( (endpoint: InferenceAPIConfigResponse) => endpoint.inference_id === inferenceId ) - ).to.be(true); + ).to.eql(true, `${inferenceId} not found in the GET _inference/_all response`); }); it('can delete inference endpoint', async () => { log.debug(`Deleting inference endpoint`); diff --git a/x-pack/test_serverless/api_integration/test_suites/common/index_management/inference_endpoints.ts b/x-pack/test_serverless/api_integration/test_suites/common/index_management/inference_endpoints.ts index 67609fec40c93..f5f712fc7d5a1 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/index_management/inference_endpoints.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/index_management/inference_endpoints.ts @@ -26,8 +26,7 @@ export default function ({ getService }: FtrProviderContext) { let roleAuthc: RoleCredentials; let internalReqHeader: InternalRequestHeader; - // FLAKY: https://github.com/elastic/kibana/issues/189464 - describe.skip('Inference endpoints', function () { + describe('Inference endpoints', function () { before(async () => { roleAuthc = await svlUserManager.createM2mApiKeyWithRoleScope('admin'); internalReqHeader = svlCommonApi.getInternalRequestHeader(); @@ -67,7 +66,7 @@ export default function ({ getService }: FtrProviderContext) { inferenceEndpoints.some( (endpoint: InferenceAPIConfigResponse) => endpoint.inference_id === inferenceId ) - ).to.be(true); + ).to.eql(true, `${inferenceId} not found in the GET _inference/_all response`); }); it('can delete inference endpoint', async () => { log.debug(`Deleting inference endpoint`); From fec2318ee36319e7653feb8d8253adf14b5b5290 Mon Sep 17 00:00:00 2001 From: jennypavlova Date: Thu, 1 Aug 2024 16:51:39 +0200 Subject: [PATCH 6/8] [Infra] Create new formulas for rx and tx metrics (#189281) Closes #188641 ## Summary This PR adds new formulas for rx and tx metrics for hosts. In inventory we show the old metrics as legacy and the new ones with the old metrics labels (this affects only hosts): image All old alerts should work - The only difference is that it will show the metric as "Legacy" and it still can be used in the rules. The hosts view and the lens charts are using a new formula ## Testing - Check the network metrics in the inventory / alert flyout (both the new ones and the old ones) - Check the network metrics and charts in the hosts view (only the new ones should be available) https://github.com/user-attachments/assets/886fd5a0-858c-458b-9025-eb55913b1932 https://github.com/user-attachments/assets/7752939f-f693-4021-bf23-89e264ef0c2d --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Carlos Crespo Co-authored-by: Elastic Machine --- .../formatters/snapshot_metric_formats.ts | 2 + .../http_api/infra/get_infra_metrics.ts | 2 + .../common/inventory_models/intl_strings.ts | 21 ++++---- .../infra/common/snapshot_metric_i18n.ts | 41 ++++++++++++++-- .../inventory/components/expression.tsx | 4 +- .../components/timeline/timeline.tsx | 4 +- .../metrics_and_groupby_toolbar_items.tsx | 6 ++- .../waffle/conditional_tooltip.test.tsx | 8 ++-- .../components/waffle/conditional_tooltip.tsx | 4 +- .../lib/create_inventory_metric_formatter.ts | 2 + .../inventory_metric_threshold_executor.ts | 48 +++++++++++++------ .../common/inventory_models/host/index.ts | 2 +- .../host/metrics/formulas/network.ts | 8 +--- .../host/metrics/snapshot/index.ts | 4 ++ .../host/metrics/snapshot/rx_v2.ts | 39 +++++++++++++++ .../host/metrics/snapshot/tx_v2.ts | 39 +++++++++++++++ .../common/inventory_models/types.ts | 4 +- .../api_integration/apis/metrics_ui/infra.ts | 2 +- .../test/functional/apps/infra/home_page.ts | 2 - .../test/functional/apps/infra/hosts_view.ts | 1 + .../page_objects/infra_hosts_view.ts | 2 - 21 files changed, 193 insertions(+), 52 deletions(-) create mode 100644 x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/snapshot/rx_v2.ts create mode 100644 x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/snapshot/tx_v2.ts diff --git a/x-pack/plugins/observability_solution/infra/common/formatters/snapshot_metric_formats.ts b/x-pack/plugins/observability_solution/infra/common/formatters/snapshot_metric_formats.ts index 1715a28b1caab..72c68d47e9776 100644 --- a/x-pack/plugins/observability_solution/infra/common/formatters/snapshot_metric_formats.ts +++ b/x-pack/plugins/observability_solution/infra/common/formatters/snapshot_metric_formats.ts @@ -35,6 +35,8 @@ export const METRIC_FORMATTERS: MetricFormatters = { }, ['rx']: { formatter: InfraFormatterType.bits, template: '{{value}}/s' }, ['tx']: { formatter: InfraFormatterType.bits, template: '{{value}}/s' }, + ['rxV2']: { formatter: InfraFormatterType.bits, template: '{{value}}/s' }, + ['txV2']: { formatter: InfraFormatterType.bits, template: '{{value}}/s' }, ['logRate']: { formatter: InfraFormatterType.abbreviatedNumber, template: '{{value}}/s', diff --git a/x-pack/plugins/observability_solution/infra/common/http_api/infra/get_infra_metrics.ts b/x-pack/plugins/observability_solution/infra/common/http_api/infra/get_infra_metrics.ts index 03114642146ff..8cbd09470ee71 100644 --- a/x-pack/plugins/observability_solution/infra/common/http_api/infra/get_infra_metrics.ts +++ b/x-pack/plugins/observability_solution/infra/common/http_api/infra/get_infra_metrics.ts @@ -16,6 +16,8 @@ export const InfraMetricTypeRT = rt.keyof({ memoryFree: null, rx: null, tx: null, + rxV2: null, + txV2: null, }); export const RangeRT = rt.type({ diff --git a/x-pack/plugins/observability_solution/infra/common/inventory_models/intl_strings.ts b/x-pack/plugins/observability_solution/infra/common/inventory_models/intl_strings.ts index 5a90113c9069e..c8e0c4d0d2ae3 100644 --- a/x-pack/plugins/observability_solution/infra/common/inventory_models/intl_strings.ts +++ b/x-pack/plugins/observability_solution/infra/common/inventory_models/intl_strings.ts @@ -6,7 +6,11 @@ */ import { i18n } from '@kbn/i18n'; -import { SnapshotMetricType, SnapshotMetricTypeKeys } from '@kbn/metrics-data-access-plugin/common'; +import { + type InventoryItemType, + type SnapshotMetricType, + SnapshotMetricTypeKeys, +} from '@kbn/metrics-data-access-plugin/common'; import { toMetricOpt } from '../snapshot_metric_i18n'; interface Lookup { @@ -44,10 +48,11 @@ export const fieldToName = (field: string) => { }; const snapshotTypeKeys = Object.keys(SnapshotMetricTypeKeys) as SnapshotMetricType[]; -export const SNAPSHOT_METRIC_TRANSLATIONS = snapshotTypeKeys.reduce((result, metric) => { - const text = toMetricOpt(metric)?.text; - if (text) { - result[metric] = text; - } - return result; -}, {} as Record); +export const getSnapshotMetricTranslations = (nodeType: InventoryItemType) => + snapshotTypeKeys.reduce((result, metric) => { + const text = toMetricOpt(metric, nodeType)?.text; + if (text) { + result[metric] = text; + } + return result; + }, {} as Record); diff --git a/x-pack/plugins/observability_solution/infra/common/snapshot_metric_i18n.ts b/x-pack/plugins/observability_solution/infra/common/snapshot_metric_i18n.ts index 3dc948d7bc82d..0561ea0c3add1 100644 --- a/x-pack/plugins/observability_solution/infra/common/snapshot_metric_i18n.ts +++ b/x-pack/plugins/observability_solution/infra/common/snapshot_metric_i18n.ts @@ -7,7 +7,7 @@ import { i18n } from '@kbn/i18n'; import { mapValues } from 'lodash'; -import { SnapshotMetricType } from '@kbn/metrics-data-access-plugin/common'; +import type { InventoryItemType, SnapshotMetricType } from '@kbn/metrics-data-access-plugin/common'; // Lowercase versions of all metrics, for when they need to be used in the middle of a sentence; // these may need to be translated differently depending on language, e.g. still capitalizing "CPU" @@ -28,6 +28,14 @@ const TranslationsLowercase = { defaultMessage: 'outbound traffic', }), + InboundTrafficLegacy: i18n.translate('xpack.infra.waffle.metricOptions.inboundTrafficText', { + defaultMessage: 'inbound traffic (Legacy)', + }), + + OutboundTrafficLegacy: i18n.translate('xpack.infra.waffle.metricOptions.outboundTrafficText', { + defaultMessage: 'outbound traffic (Legacy)', + }), + LogRate: i18n.translate('xpack.infra.waffle.metricOptions.hostLogRateText', { defaultMessage: 'log rate', }), @@ -94,8 +102,11 @@ const Translations = mapValues( (translation) => `${translation[0].toUpperCase()}${translation.slice(1)}` ); +const showLegacyLabel = (nodeType?: InventoryItemType) => nodeType === 'host'; + export const toMetricOpt = ( - metric: SnapshotMetricType + metric: SnapshotMetricType, + nodeType?: InventoryItemType ): { text: string; textLC: string; value: SnapshotMetricType } | undefined => { switch (metric) { case 'cpu': @@ -112,15 +123,35 @@ export const toMetricOpt = ( }; case 'rx': return { - text: Translations.InboundTraffic, - textLC: TranslationsLowercase.InboundTraffic, + text: showLegacyLabel(nodeType) + ? Translations.InboundTrafficLegacy + : Translations.InboundTraffic, + textLC: showLegacyLabel(nodeType) + ? TranslationsLowercase.InboundTrafficLegacy + : TranslationsLowercase.InboundTraffic, value: 'rx', }; case 'tx': + return { + text: showLegacyLabel(nodeType) + ? Translations.OutboundTrafficLegacy + : Translations.OutboundTraffic, + textLC: showLegacyLabel(nodeType) + ? TranslationsLowercase.OutboundTrafficLegacy + : TranslationsLowercase.OutboundTraffic, + value: 'tx', + }; + case 'rxV2': + return { + text: Translations.InboundTraffic, + textLC: TranslationsLowercase.InboundTraffic, + value: 'rxV2', + }; + case 'txV2': return { text: Translations.OutboundTraffic, textLC: TranslationsLowercase.OutboundTraffic, - value: 'tx', + value: 'txV2', }; case 'logRate': return { diff --git a/x-pack/plugins/observability_solution/infra/public/alerting/inventory/components/expression.tsx b/x-pack/plugins/observability_solution/infra/public/alerting/inventory/components/expression.tsx index 18bd2f1d3711f..c8e537621c81b 100644 --- a/x-pack/plugins/observability_solution/infra/public/alerting/inventory/components/expression.tsx +++ b/x-pack/plugins/observability_solution/infra/public/alerting/inventory/components/expression.tsx @@ -577,7 +577,7 @@ export const ExpressionRow: FC> = (props) myMetrics = containerSnapshotMetricTypes; break; } - return myMetrics.map(toMetricOpt); + return myMetrics.map((myMetric) => toMetricOpt(myMetric, props.nodeType)); }, [props.nodeType]); return ( @@ -775,6 +775,8 @@ const metricUnit: Record = { memory: { label: '%' }, rx: { label: 'bits/s' }, tx: { label: 'bits/s' }, + rxV2: { label: 'bits/s' }, + txV2: { label: 'bits/s' }, logRate: { label: '/s' }, diskIOReadBytes: { label: 'bytes/s' }, diskIOWriteBytes: { label: 'bytes/s' }, diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/timeline/timeline.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/timeline/timeline.tsx index 78c5ea3371872..545bc57c65315 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/timeline/timeline.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/timeline/timeline.tsx @@ -113,8 +113,8 @@ export const Timeline: React.FC = ({ interval, yAxisFormatter, isVisible } }, [nodeType, metricsHostsAnomalies, metricsK8sAnomalies]); - const metricLabel = toMetricOpt(metric.type)?.textLC; - const metricPopoverLabel = toMetricOpt(metric.type)?.text; + const metricLabel = toMetricOpt(metric.type, nodeType)?.textLC; + const metricPopoverLabel = toMetricOpt(metric.type, nodeType)?.text; const chartMetric = { color: Color.color0, diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/toolbars/metrics_and_groupby_toolbar_items.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/toolbars/metrics_and_groupby_toolbar_items.tsx index cdfefd037e0c7..81a82bd93f766 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/toolbars/metrics_and_groupby_toolbar_items.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/toolbars/metrics_and_groupby_toolbar_items.tsx @@ -23,8 +23,10 @@ interface Props extends ToolbarProps { export const MetricsAndGroupByToolbarItems = (props: Props) => { const metricOptions = useMemo( () => - props.metricTypes.map(toMetricOpt).filter((v) => v) as Array<{ text: string; value: string }>, - [props.metricTypes] + props.metricTypes + .map((metric) => toMetricOpt(metric, props.nodeType)) + .filter((v) => v) as Array<{ text: string; value: string }>, + [props.metricTypes, props.nodeType] ); const groupByOptions = useMemo( diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/conditional_tooltip.test.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/conditional_tooltip.test.tsx index 8ad60dad6a33c..439999d5b143f 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/conditional_tooltip.test.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/conditional_tooltip.test.tsx @@ -47,8 +47,8 @@ describe('ConditionalToolTip', () => { metrics: [ { name: 'cpu', value: 0.1, avg: 0.4, max: 0.7 }, { name: 'memory', value: 0.8, avg: 0.8, max: 1 }, - { name: 'tx', value: 1000000, avg: 1000000, max: 1000000 }, - { name: 'rx', value: 1000000, avg: 1000000, max: 1000000 }, + { name: 'txV2', value: 1000000, avg: 1000000, max: 1000000 }, + { name: 'rxV2', value: 1000000, avg: 1000000, max: 1000000 }, { name: 'cedd6ca0-5775-11eb-a86f-adb714b6c486', max: 0.34164999922116596, @@ -80,8 +80,8 @@ describe('ConditionalToolTip', () => { const expectedMetrics = [ { type: 'cpu' }, { type: 'memory' }, - { type: 'tx' }, - { type: 'rx' }, + { type: 'txV2' }, + { type: 'rxV2' }, { aggregation: 'avg', field: 'host.cpu.pct', diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/conditional_tooltip.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/conditional_tooltip.tsx index 0140dd54e77b2..0558dd6ad67ee 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/conditional_tooltip.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/conditional_tooltip.tsx @@ -20,7 +20,7 @@ import { useSourceContext } from '../../../../../containers/metrics_source'; import { InfraWaffleMapNode } from '../../../../../common/inventory/types'; import { useSnapshot } from '../../hooks/use_snaphot'; import { createInventoryMetricFormatter } from '../../lib/create_inventory_metric_formatter'; -import { SNAPSHOT_METRIC_TRANSLATIONS } from '../../../../../../common/inventory_models/intl_strings'; +import { getSnapshotMetricTranslations } from '../../../../../../common/inventory_models/intl_strings'; import { useWaffleOptionsContext } from '../../hooks/use_waffle_options'; import { createFormatterForMetric } from '../../../metrics_explorer/components/helpers/create_formatter_for_metric'; @@ -86,7 +86,7 @@ export const ConditionalToolTip = ({ node, nodeType, currentTime }: Props) => { ) : ( metrics.map((metric) => { const metricName = SnapshotMetricTypeRT.is(metric.name) ? metric.name : 'custom'; - const name = SNAPSHOT_METRIC_TRANSLATIONS[metricName] || metricName; + const name = getSnapshotMetricTranslations(nodeType)[metricName] || metricName; // if custom metric, find field and label from waffleOptionsContext result // because useSnapshot does not return it const customMetric = diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/lib/create_inventory_metric_formatter.ts b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/lib/create_inventory_metric_formatter.ts index 934d17a570080..056109587d36d 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/lib/create_inventory_metric_formatter.ts +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/lib/create_inventory_metric_formatter.ts @@ -65,6 +65,8 @@ const METRIC_FORMATTERS: MetricFormatters = { }, rx: { formatter: InfraFormatterType.bits, template: '{{value}}/s' }, tx: { formatter: InfraFormatterType.bits, template: '{{value}}/s' }, + rxV2: { formatter: InfraFormatterType.bits, template: '{{value}}/s' }, + txV2: { formatter: InfraFormatterType.bits, template: '{{value}}/s' }, logRate: { formatter: InfraFormatterType.abbreviatedNumber, template: '{{value}}/s', diff --git a/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts b/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts index 94879de3f8070..e6ed1750eea3a 100644 --- a/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts +++ b/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts @@ -22,7 +22,7 @@ import { } from '@kbn/alerting-plugin/common'; import { AlertsClientError, RuleExecutorOptions, RuleTypeState } from '@kbn/alerting-plugin/server'; import { convertToBuiltInComparators, getAlertUrl } from '@kbn/observability-plugin/common'; -import { SnapshotMetricType } from '@kbn/metrics-data-access-plugin/common'; +import type { InventoryItemType, SnapshotMetricType } from '@kbn/metrics-data-access-plugin/common'; import { ObservabilityMetricsAlert } from '@kbn/alerts-as-data-utils'; import { getOriginalActionGroup } from '../../../utils/get_original_action_group'; import { @@ -226,12 +226,13 @@ export const createInventoryMetricThresholdExecutor = if (nextState === AlertStates.ALERT || nextState === AlertStates.WARNING) { reason = results .map((result) => - buildReasonWithVerboseMetricName( + buildReasonWithVerboseMetricName({ group, - result[group], - buildFiredAlertReason, - nextState === AlertStates.WARNING - ) + resultItem: result[group], + buildReason: buildFiredAlertReason, + useWarningThreshold: nextState === AlertStates.WARNING, + nodeType, + }) ) .join('\n'); } @@ -240,14 +241,24 @@ export const createInventoryMetricThresholdExecutor = reason = results .filter((result) => result[group].isNoData) .map((result) => - buildReasonWithVerboseMetricName(group, result[group], buildNoDataAlertReason) + buildReasonWithVerboseMetricName({ + group, + resultItem: result[group], + buildReason: buildNoDataAlertReason, + nodeType, + }) ) .join('\n'); } else if (nextState === AlertStates.ERROR) { reason = results .filter((result) => result[group].isError) .map((result) => - buildReasonWithVerboseMetricName(group, result[group], buildErrorAlertReason) + buildReasonWithVerboseMetricName({ + group, + resultItem: result[group], + buildReason: buildErrorAlertReason, + nodeType, + }) ) .join('\n'); } @@ -384,12 +395,19 @@ const formatThreshold = (metric: SnapshotMetricType, value: number | number[]) = return threshold; }; -const buildReasonWithVerboseMetricName = ( - group: string, - resultItem: ConditionResult, - buildReason: (r: any) => string, - useWarningThreshold?: boolean -) => { +const buildReasonWithVerboseMetricName = ({ + group, + resultItem, + buildReason, + useWarningThreshold, + nodeType, +}: { + group: string; + resultItem: ConditionResult; + buildReason: (r: any) => string; + useWarningThreshold?: boolean; + nodeType?: InventoryItemType; +}) => { if (!resultItem) return ''; const thresholdToFormat = useWarningThreshold @@ -399,7 +417,7 @@ const buildReasonWithVerboseMetricName = ( ...resultItem, group, metric: - toMetricOpt(resultItem.metric)?.text || + toMetricOpt(resultItem.metric, nodeType)?.text || (resultItem.metric === 'custom' && resultItem.customMetric ? getCustomMetricLabel(resultItem.customMetric) : resultItem.metric), diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/index.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/index.ts index e1d4f60bc9f74..42e8a0b81e6c8 100644 --- a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/index.ts +++ b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/index.ts @@ -54,5 +54,5 @@ export const host: InventoryModel = { ...awsRequiredMetrics, ...nginxRequireMetrics, ], - tooltipMetrics: ['cpu', 'memory', 'tx', 'rx'], + tooltipMetrics: ['cpu', 'memory', 'txV2', 'rxV2'], }; diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/formulas/network.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/formulas/network.ts index d35beeb7469d0..9c45b276f3af9 100644 --- a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/formulas/network.ts +++ b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/formulas/network.ts @@ -12,20 +12,16 @@ export const rx: LensBaseLayer = { label: i18n.translate('xpack.metricsData.assetDetails.formulas.rx', { defaultMessage: 'Network Inbound (RX)', }), - value: - "average(host.network.ingress.bytes) * 8 / (max(metricset.period, kql='host.network.ingress.bytes: *') / 1000)", + value: 'sum(host.network.ingress.bytes) * 8', format: 'bits', decimals: 1, - normalizeByUnit: 's', }; export const tx: LensBaseLayer = { label: i18n.translate('xpack.metricsData.assetDetails.formulas.tx', { defaultMessage: 'Network Outbound (TX)', }), - value: - "average(host.network.egress.bytes) * 8 / (max(metricset.period, kql='host.network.egress.bytes: *') / 1000)", + value: 'sum(host.network.egress.bytes) * 8', format: 'bits', decimals: 1, - normalizeByUnit: 's', }; diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/snapshot/index.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/snapshot/index.ts index 8cf567b40165e..e6f1cf6ac1912 100644 --- a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/snapshot/index.ts +++ b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/snapshot/index.ts @@ -17,6 +17,8 @@ import { memoryTotal } from './memory_total'; import { normalizedLoad1m } from './normalized_load_1m'; import { rx } from './rx'; import { tx } from './tx'; +import { txV2 } from './tx_v2'; +import { rxV2 } from './rx_v2'; export const snapshot = { cpu, @@ -31,4 +33,6 @@ export const snapshot = { normalizedLoad1m, rx, tx, + rxV2, + txV2, }; diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/snapshot/rx_v2.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/snapshot/rx_v2.ts new file mode 100644 index 0000000000000..3f8466010a518 --- /dev/null +++ b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/snapshot/rx_v2.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 + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { MetricsUIAggregation } from '../../../types'; +export const rxV2: MetricsUIAggregation = { + rx_sum: { + sum: { + field: 'host.network.ingress.bytes', + }, + }, + min_timestamp: { + min: { + field: '@timestamp', + }, + }, + max_timestamp: { + max: { + field: '@timestamp', + }, + }, + rxV2: { + bucket_script: { + buckets_path: { + value: 'rx_sum', + minTime: 'min_timestamp', + maxTime: 'max_timestamp', + }, + script: { + source: 'params.value / ((params.maxTime - params.minTime) / 1000)', + lang: 'painless', + }, + gap_policy: 'skip', + }, + }, +}; diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/snapshot/tx_v2.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/snapshot/tx_v2.ts new file mode 100644 index 0000000000000..100bd3d0bf306 --- /dev/null +++ b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/snapshot/tx_v2.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 + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { MetricsUIAggregation } from '../../../types'; +export const txV2: MetricsUIAggregation = { + tx_sum: { + sum: { + field: 'host.network.egress.bytes', + }, + }, + min_timestamp: { + min: { + field: '@timestamp', + }, + }, + max_timestamp: { + max: { + field: '@timestamp', + }, + }, + txV2: { + bucket_script: { + buckets_path: { + value: 'tx_sum', + minTime: 'min_timestamp', + maxTime: 'max_timestamp', + }, + script: { + source: 'params.value / ((params.maxTime - params.minTime) / 1000)', + lang: 'painless', + }, + gap_policy: 'skip', + }, + }, +}; diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/types.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/types.ts index 4967324be21fc..c9accaca490f6 100644 --- a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/types.ts +++ b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/types.ts @@ -358,6 +358,8 @@ export const SnapshotMetricTypeKeys = { normalizedLoad1m: null, tx: null, rx: null, + txV2: null, + rxV2: null, logRate: null, diskIOReadBytes: null, diskIOWriteBytes: null, @@ -385,7 +387,7 @@ export interface InventoryMetrics { tsvb: { [name: string]: TSVBMetricModelCreator }; snapshot: { [name: string]: MetricsUIAggregation | undefined }; defaultSnapshot: SnapshotMetricType; - /** This is used by the inventory view to calculate the appropriate amount of time for the metrics detail page. Some metris like awsS3 require multiple days where others like host only need an hour.*/ + /** This is used by the inventory view to calculate the appropriate amount of time for the metrics detail page. Some metrics like awsS3 require multiple days where others like host only need an hour.*/ defaultTimeRangeInSeconds: number; } diff --git a/x-pack/test/api_integration/apis/metrics_ui/infra.ts b/x-pack/test/api_integration/apis/metrics_ui/infra.ts index f70cd61dc7635..dfb6744a94583 100644 --- a/x-pack/test/api_integration/apis/metrics_ui/infra.ts +++ b/x-pack/test/api_integration/apis/metrics_ui/infra.ts @@ -246,7 +246,7 @@ export default function ({ getService }: FtrProviderContext) { const response = await makeRequest({ invalidBody, expectedHTTPCode: 400 }); expect(normalizeNewLine(response.body.message)).to.be( - '[request body]: Failed to validate: in metrics/0/type: "any" does not match expected type "cpu" | "normalizedLoad1m" | "diskSpaceUsage" | "memory" | "memoryFree" | "rx" | "tx"' + '[request body]: Failed to validate: in metrics/0/type: "any" does not match expected type "cpu" | "normalizedLoad1m" | "diskSpaceUsage" | "memory" | "memoryFree" | "rx" | "tx" | "rxV2" | "txV2"' ); }); diff --git a/x-pack/test/functional/apps/infra/home_page.ts b/x-pack/test/functional/apps/infra/home_page.ts index 7751359ac30fd..17781fa2cd836 100644 --- a/x-pack/test/functional/apps/infra/home_page.ts +++ b/x-pack/test/functional/apps/infra/home_page.ts @@ -573,8 +573,6 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { 'system.core.steal.pct', 'system.cpu.nice.pct', 'system.cpu.idle.pct', - 'system.cpu.iowait.pct', - 'system.cpu.irq.pct', ]; for (const field of fields) { diff --git a/x-pack/test/functional/apps/infra/hosts_view.ts b/x-pack/test/functional/apps/infra/hosts_view.ts index 8af7d9fc23dc3..6578a7911bf03 100644 --- a/x-pack/test/functional/apps/infra/hosts_view.ts +++ b/x-pack/test/functional/apps/infra/hosts_view.ts @@ -560,6 +560,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { it('should have an option to open the chart in lens', async () => { await retry.try(async () => { await pageObjects.infraHostsView.clickAndValidateMetricChartActionOptions(); + await browser.pressKeys(browser.keys.ESCAPE); }); }); }); diff --git a/x-pack/test/functional/page_objects/infra_hosts_view.ts b/x-pack/test/functional/page_objects/infra_hosts_view.ts index 18407b72da412..e302057d5c3ec 100644 --- a/x-pack/test/functional/page_objects/infra_hosts_view.ts +++ b/x-pack/test/functional/page_objects/infra_hosts_view.ts @@ -129,8 +129,6 @@ export function InfraHostsViewProvider({ getService }: FtrProviderContext) { const button = await element.findByTestSubject('embeddablePanelToggleMenuIcon'); await button.click(); await testSubjects.existOrFail('embeddablePanelAction-openInLens'); - // forces the modal to close - await element.click(); }, // KPIs From b6b5a89fa4863cf72d343cef7804071f82484949 Mon Sep 17 00:00:00 2001 From: Melissa Alvarez Date: Thu, 1 Aug 2024 08:53:05 -0600 Subject: [PATCH 7/8] [ML] Job selection flyout: UX improvements (#189239) ## Summary Related meta issue: https://github.com/elastic/kibana/issues/182042 Fixes https://github.com/elastic/kibana/issues/186228 This PR makes some small UX improvements to the Job selection flyout: - replaces the callout with the EuiEmptyPrompt - the Primary action (Apply) is now on the right of the footer and the Secondary action (Close) is aligned left image image In dashboard, shows the empty prompt when no jobs in the panel config flyout: image ### Checklist Delete any items that are not applicable to this PR. - [ ] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md) - [ ] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - [ ] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [ ] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed - [ ] Any UI touched in this PR is usable by keyboard only (learn more about [keyboard accessibility](https://webaim.org/techniques/keyboard/)) - [ ] Any UI touched in this PR does not create any new axe failures (run axe in browser: [FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/), [Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US)) - [ ] If a plugin configuration key changed, check if it needs to be allowlisted in the cloud and added to the [docker list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker) - [ ] This renders correctly on smaller devices using a responsive layout. (You can test this [in your browser](https://www.browserstack.com/guide/responsive-testing-on-local-server)) - [ ] This was checked for [cross-browser compatibility](https://www.elastic.co/support/matrix#matrix_browsers) --------- Co-authored-by: Elastic Machine --- .../ml_anomaly_alert_trigger.tsx | 1 + .../ml/public/alerting/job_selector.tsx | 42 +++++++- ...aly_detection_jobs_health_rule_trigger.tsx | 2 + .../job_selector/job_selector_flyout.tsx | 34 ++++--- .../job_selector_table/job_selector_table.js | 26 ++--- .../anomaly_charts_initializer.tsx | 94 +++++++++--------- .../anomaly_swimlane_initializer.tsx | 96 ++++++++++--------- .../single_metric_viewer_initializer.tsx | 44 +++++---- 8 files changed, 198 insertions(+), 141 deletions(-) diff --git a/x-pack/plugins/ml/public/alerting/anomaly_detection_rule/ml_anomaly_alert_trigger.tsx b/x-pack/plugins/ml/public/alerting/anomaly_detection_rule/ml_anomaly_alert_trigger.tsx index d885d04053441..2f36f3f687caf 100644 --- a/x-pack/plugins/ml/public/alerting/anomaly_detection_rule/ml_anomaly_alert_trigger.tsx +++ b/x-pack/plugins/ml/public/alerting/anomaly_detection_rule/ml_anomaly_alert_trigger.tsx @@ -197,6 +197,7 @@ const MlAnomalyAlertTrigger: FC = ({ // eslint-disable-next-line react-hooks/exhaustive-deps onChange={useCallback(onAlertParamChange('jobSelection'), [])} errors={Array.isArray(errors.jobSelection) ? errors.jobSelection : []} + shouldUseDropdownJobCreate /> >; + /** + * Flag to indicate whether to use the job creation button in the empty prompt or the dropdown when no jobs are available. + */ + shouldUseDropdownJobCreate?: boolean; } export const JobSelectorControl: FC = ({ @@ -55,6 +60,7 @@ export const JobSelectorControl: FC = ({ allowSelectAll = false, createJobUrl, options: defaultOptions, + shouldUseDropdownJobCreate = false, }) => { const { services: { @@ -66,6 +72,7 @@ export const JobSelectorControl: FC = ({ const isMounted = useMountedState(); const [options, setOptions] = useState>>([]); + const [areJobsLoading, setAreJobsLoading] = useState(false); const jobIds = useMemo(() => new Set(), []); const groupIds = useMemo(() => new Set(), []); @@ -78,6 +85,7 @@ export const JobSelectorControl: FC = ({ ); const fetchOptions = useCallback(async () => { + setAreJobsLoading(true); try { const { jobIds: jobIdOptions, groupIds: groupIdOptions } = await adJobsApiService.getAllJobAndGroupIds(); @@ -147,6 +155,7 @@ export const JobSelectorControl: FC = ({ }), }); } + setAreJobsLoading(false); }, [ adJobsApiService, allowSelectAll, @@ -200,7 +209,9 @@ export const JobSelectorControl: FC = ({ // eslint-disable-next-line react-hooks/exhaustive-deps }, [createJobUrl]); - return ( + if (areJobsLoading === true) return ; + + return jobIds.size || shouldUseDropdownJobCreate ? ( = ({ isInvalid={!!errors?.length} /> + ) : ( + + + + } + body={ + navigateToUrl(createJobUrl!)} + disabled={createJobUrl === undefined} + > + + + } + /> ); }; diff --git a/x-pack/plugins/ml/public/alerting/jobs_health_rule/anomaly_detection_jobs_health_rule_trigger.tsx b/x-pack/plugins/ml/public/alerting/jobs_health_rule/anomaly_detection_jobs_health_rule_trigger.tsx index 9f89e049b937f..4e8d88f8b33ef 100644 --- a/x-pack/plugins/ml/public/alerting/jobs_health_rule/anomaly_detection_jobs_health_rule_trigger.tsx +++ b/x-pack/plugins/ml/public/alerting/jobs_health_rule/anomaly_detection_jobs_health_rule_trigger.tsx @@ -123,6 +123,7 @@ const AnomalyDetectionJobsHealthRuleTrigger: FC = ({ defaultMessage="Include jobs or groups" /> } + shouldUseDropdownJobCreate /> @@ -148,6 +149,7 @@ const AnomalyDetectionJobsHealthRuleTrigger: FC = ({ /> } options={excludeJobsOptions} + shouldUseDropdownJobCreate /> diff --git a/x-pack/plugins/ml/public/application/components/job_selector/job_selector_flyout.tsx b/x-pack/plugins/ml/public/application/components/job_selector/job_selector_flyout.tsx index 6d4ca53f72d51..b4da44047fd86 100644 --- a/x-pack/plugins/ml/public/application/components/job_selector/job_selector_flyout.tsx +++ b/x-pack/plugins/ml/public/application/components/job_selector/job_selector_flyout.tsx @@ -242,7 +242,9 @@ export const JobSelectorFlyoutContent: FC = ({ )} - {withTimeRangeSelector && applyTimeRangeConfig !== undefined && ( + {withTimeRangeSelector && + applyTimeRangeConfig !== undefined && + jobs.length !== 0 ? ( = ({ data-test-subj="mlFlyoutJobSelectorSwitchApplyTimeRange" /> - )} + ) : null} @@ -278,19 +280,7 @@ export const JobSelectorFlyoutContent: FC = ({ - - - - {i18n.translate('xpack.ml.jobSelector.applyFlyoutButton', { - defaultMessage: 'Apply', - })} - - + = ({ })} + + {jobs.length !== 0 ? ( + + {i18n.translate('xpack.ml.jobSelector.applyFlyoutButton', { + defaultMessage: 'Apply', + })} + + ) : null} + diff --git a/x-pack/plugins/ml/public/application/components/job_selector/job_selector_table/job_selector_table.js b/x-pack/plugins/ml/public/application/components/job_selector/job_selector_table/job_selector_table.js index 1afed1eaac8ca..b9086c31beded 100644 --- a/x-pack/plugins/ml/public/application/components/job_selector/job_selector_table/job_selector_table.js +++ b/x-pack/plugins/ml/public/application/components/job_selector/job_selector_table/job_selector_table.js @@ -13,12 +13,11 @@ import { TimeRangeBar } from '../timerange_bar'; import { FormattedMessage } from '@kbn/i18n-react'; import { + EuiEmptyPrompt, EuiFlexGroup, EuiFlexItem, EuiTabbedContent, - EuiCallOut, EuiButton, - EuiText, LEFT_ALIGNMENT, CENTER_ALIGNMENT, SortableProperties, @@ -265,17 +264,20 @@ export function JobSelectorTable({ {jobs.length === 0 && ( - +

+ +

} - iconType="iInCircle" - > - + body={ - -
+ } + /> )} {jobs.length !== 0 && singleSelection === true && renderJobsTable()} {jobs.length !== 0 && !singleSelection && renderTabs()} diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_initializer.tsx b/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_initializer.tsx index 536084cc01496..93bf3110b3776 100644 --- a/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_initializer.tsx +++ b/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_initializer.tsx @@ -101,57 +101,59 @@ export const AnomalyChartsInitializer: FC = ({ errors={jobIdsErrors} /> - - - } - isInvalid={!isPanelTitleValid} - > - { - titleManuallyChanged.current = true; - setPanelTitle(e.target.value); - }} + {jobIds.length > 0 ? ( + + + } isInvalid={!isPanelTitleValid} - /> - + > + { + titleManuallyChanged.current = true; + setPanelTitle(e.target.value); + }} + isInvalid={!isPanelTitleValid} + /> + - + ) : undefined + } + label={ - ) : undefined - } - label={ - + setMaxSeriesToPlot(parseInt(e.target.value, 10))} + min={1} + max={MAX_ANOMALY_CHARTS_ALLOWED} /> - } - > - setMaxSeriesToPlot(parseInt(e.target.value, 10))} - min={1} - max={MAX_ANOMALY_CHARTS_ALLOWED} - /> - - + + + ) : null} diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_initializer.tsx b/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_initializer.tsx index 52c84770da12c..ceb22e0172c8d 100644 --- a/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_initializer.tsx +++ b/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_initializer.tsx @@ -157,52 +157,58 @@ export const AnomalySwimlaneInitializer: FC = ( }} errors={jobIdsErrors} /> + {jobIds.length > 0 ? ( + <> + + } + isInvalid={!isPanelTitleValid} + fullWidth + > + { + titleManuallyChanged.current = true; + setPanelTitle(e.target.value); + }} + isInvalid={!isPanelTitleValid} + fullWidth + /> + - - } - isInvalid={!isPanelTitleValid} - fullWidth - > - { - titleManuallyChanged.current = true; - setPanelTitle(e.target.value); - }} - isInvalid={!isPanelTitleValid} - fullWidth - /> - - - - } - fullWidth - > - setSwimlaneType(id as SwimlaneType)} - /> - + + } + fullWidth + > + setSwimlaneType(id as SwimlaneType)} + /> + + + ) : null} {swimlaneType === SWIMLANE_TYPE.VIEW_BY && ( <> diff --git a/x-pack/plugins/ml/public/embeddables/single_metric_viewer/single_metric_viewer_initializer.tsx b/x-pack/plugins/ml/public/embeddables/single_metric_viewer/single_metric_viewer_initializer.tsx index 4e2e338eee8ee..9f21ca3f4af75 100644 --- a/x-pack/plugins/ml/public/embeddables/single_metric_viewer/single_metric_viewer_initializer.tsx +++ b/x-pack/plugins/ml/public/embeddables/single_metric_viewer/single_metric_viewer_initializer.tsx @@ -150,29 +150,31 @@ export const SingleMetricViewerInitializer: FC - - } - isInvalid={!isPanelTitleValid} - fullWidth - > - { - titleManuallyChanged.current = true; - setPanelTitle(e.target.value); - }} + {job?.job_id && jobId && jobId === job.job_id ? ( + + } isInvalid={!isPanelTitleValid} fullWidth - /> - + > + { + titleManuallyChanged.current = true; + setPanelTitle(e.target.value); + }} + isInvalid={!isPanelTitleValid} + fullWidth + /> + + ) : null} {job?.job_id && jobId && jobId === job.job_id ? ( Date: Thu, 1 Aug 2024 17:14:42 +0200 Subject: [PATCH 8/8] [Infra] Move apm common types to a package (#189649) part of [#188752](https://github.com/elastic/kibana/issues/188752) ## Summary Move some types to `kbn-apm-types`. These types will be used later in the `apm-data-access` plugin https://github.com/elastic/kibana/pull/189654. --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .github/CODEOWNERS | 1 + package.json | 1 + packages/kbn-apm-types/es_fields.ts | 9 + packages/kbn-apm-types/es_schemas_raw.ts | 9 + packages/kbn-apm-types/es_schemas_ui.ts | 10 + packages/kbn-apm-types/index.ts | 11 + packages/kbn-apm-types/kibana.jsonc | 5 + packages/kbn-apm-types/package.json | 6 + packages/kbn-apm-types/src/es_fields/apm.ts | 197 ++++++++++++++++++ .../src/es_schemas/raw/apm_base_doc.ts | 24 +++ .../src/es_schemas/raw/error_raw.ts | 75 +++++++ .../src/es_schemas/raw/event_raw.ts | 26 +++ .../src/es_schemas/raw/fields/cloud.ts | 34 +++ .../src/es_schemas/raw/fields/container.ts | 12 ++ .../es_schemas/raw/fields/event_outcome.ts | 9 + .../src/es_schemas/raw/fields/faas.ts | 17 ++ .../src/es_schemas/raw/fields/host.ts | 17 ++ .../src/es_schemas/raw/fields/http.ts | 13 ++ .../src/es_schemas/raw/fields/index.ts | 25 +++ .../src/es_schemas/raw/fields/kubernetes.ts | 22 ++ .../src/es_schemas/raw/fields/observer.ts | 17 ++ .../src/es_schemas/raw/fields/page.ts | 12 ++ .../src/es_schemas/raw/fields/process.ts | 14 ++ .../src/es_schemas/raw/fields/service.ts | 28 +++ .../src/es_schemas/raw/fields/span_links.ts | 12 ++ .../src/es_schemas/raw/fields/stackframe.ts | 44 ++++ .../src/es_schemas/raw/fields/timestamp_us.ts | 11 + .../src/es_schemas/raw/fields/url.ts | 13 ++ .../src/es_schemas/raw/fields/user.ts | 11 + .../src/es_schemas/raw/fields/user_agent.ts | 21 ++ .../kbn-apm-types/src/es_schemas/raw/index.ts | 14 ++ .../src/es_schemas/raw/metric_raw.ts | 124 +++++++++++ .../src/es_schemas/raw/span_raw.ts | 80 +++++++ .../src/es_schemas/raw/transaction_raw.ts | 79 +++++++ .../src/es_schemas/ui/apm_error.ts | 14 ++ .../kbn-apm-types/src/es_schemas/ui/event.ts | 14 ++ .../src/es_schemas/ui/fields/agent.ts | 17 ++ .../src/es_schemas/ui/fields/index.ts | 8 + .../kbn-apm-types/src/es_schemas/ui/index.ts | 13 ++ .../kbn-apm-types/src/es_schemas/ui/metric.ts | 11 + .../kbn-apm-types/src/es_schemas/ui/span.ts | 14 ++ .../src/es_schemas/ui/transaction.ts | 23 ++ packages/kbn-apm-types/tsconfig.json | 18 ++ tsconfig.base.json | 2 + .../apm/common/es_fields/apm.ts | 189 +---------------- .../observability_solution/apm/tsconfig.json | 3 +- .../typings/es_schemas/raw/apm_base_doc.ts | 17 +- .../apm/typings/es_schemas/raw/error_raw.ts | 66 +----- .../apm/typings/es_schemas/raw/event_raw.ts | 19 +- .../typings/es_schemas/raw/fields/cloud.ts | 27 +-- .../es_schemas/raw/fields/container.ts | 5 +- .../es_schemas/raw/fields/event_outcome.ts | 2 +- .../apm/typings/es_schemas/raw/fields/faas.ts | 10 +- .../apm/typings/es_schemas/raw/fields/host.ts | 10 +- .../apm/typings/es_schemas/raw/fields/http.ts | 6 +- .../es_schemas/raw/fields/kubernetes.ts | 15 +- .../typings/es_schemas/raw/fields/observer.ts | 10 +- .../apm/typings/es_schemas/raw/fields/page.ts | 4 +- .../typings/es_schemas/raw/fields/process.ts | 7 +- .../typings/es_schemas/raw/fields/service.ts | 21 +- .../es_schemas/raw/fields/span_links.ts | 5 +- .../es_schemas/raw/fields/stackframe.ts | 37 +--- .../es_schemas/raw/fields/timestamp_us.ts | 4 +- .../apm/typings/es_schemas/raw/fields/url.ts | 6 +- .../apm/typings/es_schemas/raw/fields/user.ts | 4 +- .../es_schemas/raw/fields/user_agent.ts | 14 +- .../apm/typings/es_schemas/raw/metric_raw.ts | 117 +---------- .../apm/typings/es_schemas/raw/span_raw.ts | 73 +------ .../typings/es_schemas/raw/transaction_raw.ts | 72 +------ .../apm/typings/es_schemas/ui/apm_error.ts | 7 +- .../apm/typings/es_schemas/ui/event.ts | 7 +- .../apm/typings/es_schemas/ui/fields/agent.ts | 15 +- .../apm/typings/es_schemas/ui/metric.ts | 4 +- .../apm/typings/es_schemas/ui/span.ts | 7 +- .../apm/typings/es_schemas/ui/transaction.ts | 16 +- yarn.lock | 4 + 76 files changed, 1178 insertions(+), 762 deletions(-) create mode 100644 packages/kbn-apm-types/es_fields.ts create mode 100644 packages/kbn-apm-types/es_schemas_raw.ts create mode 100644 packages/kbn-apm-types/es_schemas_ui.ts create mode 100644 packages/kbn-apm-types/index.ts create mode 100644 packages/kbn-apm-types/kibana.jsonc create mode 100644 packages/kbn-apm-types/package.json create mode 100644 packages/kbn-apm-types/src/es_fields/apm.ts create mode 100644 packages/kbn-apm-types/src/es_schemas/raw/apm_base_doc.ts create mode 100644 packages/kbn-apm-types/src/es_schemas/raw/error_raw.ts create mode 100644 packages/kbn-apm-types/src/es_schemas/raw/event_raw.ts create mode 100644 packages/kbn-apm-types/src/es_schemas/raw/fields/cloud.ts create mode 100644 packages/kbn-apm-types/src/es_schemas/raw/fields/container.ts create mode 100644 packages/kbn-apm-types/src/es_schemas/raw/fields/event_outcome.ts create mode 100644 packages/kbn-apm-types/src/es_schemas/raw/fields/faas.ts create mode 100644 packages/kbn-apm-types/src/es_schemas/raw/fields/host.ts create mode 100644 packages/kbn-apm-types/src/es_schemas/raw/fields/http.ts create mode 100644 packages/kbn-apm-types/src/es_schemas/raw/fields/index.ts create mode 100644 packages/kbn-apm-types/src/es_schemas/raw/fields/kubernetes.ts create mode 100644 packages/kbn-apm-types/src/es_schemas/raw/fields/observer.ts create mode 100644 packages/kbn-apm-types/src/es_schemas/raw/fields/page.ts create mode 100644 packages/kbn-apm-types/src/es_schemas/raw/fields/process.ts create mode 100644 packages/kbn-apm-types/src/es_schemas/raw/fields/service.ts create mode 100644 packages/kbn-apm-types/src/es_schemas/raw/fields/span_links.ts create mode 100644 packages/kbn-apm-types/src/es_schemas/raw/fields/stackframe.ts create mode 100644 packages/kbn-apm-types/src/es_schemas/raw/fields/timestamp_us.ts create mode 100644 packages/kbn-apm-types/src/es_schemas/raw/fields/url.ts create mode 100644 packages/kbn-apm-types/src/es_schemas/raw/fields/user.ts create mode 100644 packages/kbn-apm-types/src/es_schemas/raw/fields/user_agent.ts create mode 100644 packages/kbn-apm-types/src/es_schemas/raw/index.ts create mode 100644 packages/kbn-apm-types/src/es_schemas/raw/metric_raw.ts create mode 100644 packages/kbn-apm-types/src/es_schemas/raw/span_raw.ts create mode 100644 packages/kbn-apm-types/src/es_schemas/raw/transaction_raw.ts create mode 100644 packages/kbn-apm-types/src/es_schemas/ui/apm_error.ts create mode 100644 packages/kbn-apm-types/src/es_schemas/ui/event.ts create mode 100644 packages/kbn-apm-types/src/es_schemas/ui/fields/agent.ts create mode 100644 packages/kbn-apm-types/src/es_schemas/ui/fields/index.ts create mode 100644 packages/kbn-apm-types/src/es_schemas/ui/index.ts create mode 100644 packages/kbn-apm-types/src/es_schemas/ui/metric.ts create mode 100644 packages/kbn-apm-types/src/es_schemas/ui/span.ts create mode 100644 packages/kbn-apm-types/src/es_schemas/ui/transaction.ts create mode 100644 packages/kbn-apm-types/tsconfig.json diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index a92983d9532b5..246497e110b94 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -46,6 +46,7 @@ x-pack/plugins/observability_solution/apm/ftr_e2e @elastic/obs-ux-infra_services x-pack/plugins/observability_solution/apm @elastic/obs-ux-infra_services-team packages/kbn-apm-synthtrace @elastic/obs-ux-infra_services-team @elastic/obs-ux-logs-team packages/kbn-apm-synthtrace-client @elastic/obs-ux-infra_services-team @elastic/obs-ux-logs-team +packages/kbn-apm-types @elastic/obs-ux-infra_services-team packages/kbn-apm-utils @elastic/obs-ux-infra_services-team test/plugin_functional/plugins/app_link_test @elastic/kibana-core x-pack/test/usage_collection/plugins/application_usage_test @elastic/kibana-core diff --git a/package.json b/package.json index 12e7a18f50c8f..e76a42d2d68e3 100644 --- a/package.json +++ b/package.json @@ -181,6 +181,7 @@ "@kbn/apm-data-access-plugin": "link:x-pack/plugins/observability_solution/apm_data_access", "@kbn/apm-data-view": "link:packages/kbn-apm-data-view", "@kbn/apm-plugin": "link:x-pack/plugins/observability_solution/apm", + "@kbn/apm-types": "link:packages/kbn-apm-types", "@kbn/apm-utils": "link:packages/kbn-apm-utils", "@kbn/app-link-test-plugin": "link:test/plugin_functional/plugins/app_link_test", "@kbn/application-usage-test-plugin": "link:x-pack/test/usage_collection/plugins/application_usage_test", diff --git a/packages/kbn-apm-types/es_fields.ts b/packages/kbn-apm-types/es_fields.ts new file mode 100644 index 0000000000000..00e78c5196f7d --- /dev/null +++ b/packages/kbn-apm-types/es_fields.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export * from './src/es_fields/apm'; diff --git a/packages/kbn-apm-types/es_schemas_raw.ts b/packages/kbn-apm-types/es_schemas_raw.ts new file mode 100644 index 0000000000000..ef0d4cbeb5897 --- /dev/null +++ b/packages/kbn-apm-types/es_schemas_raw.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +export * from './src/es_schemas/raw'; +export * from './src/es_schemas/raw/fields'; diff --git a/packages/kbn-apm-types/es_schemas_ui.ts b/packages/kbn-apm-types/es_schemas_ui.ts new file mode 100644 index 0000000000000..e974312a737ef --- /dev/null +++ b/packages/kbn-apm-types/es_schemas_ui.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export * from './src/es_schemas/ui'; +export * from './src/es_schemas/ui/fields'; diff --git a/packages/kbn-apm-types/index.ts b/packages/kbn-apm-types/index.ts new file mode 100644 index 0000000000000..8f15cbd897e28 --- /dev/null +++ b/packages/kbn-apm-types/index.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export * from './es_fields'; +export * from './es_schemas_raw'; +export * from './es_schemas_ui'; diff --git a/packages/kbn-apm-types/kibana.jsonc b/packages/kbn-apm-types/kibana.jsonc new file mode 100644 index 0000000000000..26b4ec0b1cf75 --- /dev/null +++ b/packages/kbn-apm-types/kibana.jsonc @@ -0,0 +1,5 @@ +{ + "type": "shared-common", + "id": "@kbn/apm-types", + "owner": "@elastic/obs-ux-infra_services-team" +} diff --git a/packages/kbn-apm-types/package.json b/packages/kbn-apm-types/package.json new file mode 100644 index 0000000000000..3b15b82701d81 --- /dev/null +++ b/packages/kbn-apm-types/package.json @@ -0,0 +1,6 @@ +{ + "name": "@kbn/apm-types", + "private": true, + "version": "1.0.0", + "license": "SSPL-1.0 OR Elastic License 2.0" +} \ No newline at end of file diff --git a/packages/kbn-apm-types/src/es_fields/apm.ts b/packages/kbn-apm-types/src/es_fields/apm.ts new file mode 100644 index 0000000000000..d17a1ed78db90 --- /dev/null +++ b/packages/kbn-apm-types/src/es_fields/apm.ts @@ -0,0 +1,197 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export const TIMESTAMP = 'timestamp.us'; +export const AGENT = 'agent'; +export const AGENT_NAME = 'agent.name'; +export const AGENT_VERSION = 'agent.version'; +export const AGENT_ACTIVATION_METHOD = 'agent.activation_method'; + +export const DESTINATION_ADDRESS = 'destination.address'; + +export const CLOUD = 'cloud'; +export const CLOUD_AVAILABILITY_ZONE = 'cloud.availability_zone'; +export const CLOUD_PROVIDER = 'cloud.provider'; +export const CLOUD_REGION = 'cloud.region'; +export const CLOUD_MACHINE_TYPE = 'cloud.machine.type'; +export const CLOUD_ACCOUNT_ID = 'cloud.account.id'; +export const CLOUD_INSTANCE_ID = 'cloud.instance.id'; +export const CLOUD_INSTANCE_NAME = 'cloud.instance.name'; +export const CLOUD_SERVICE_NAME = 'cloud.service.name'; + +export const EVENT_SUCCESS_COUNT = 'event.success_count'; + +export const SERVICE = 'service'; +export const SERVICE_NAME = 'service.name'; +export const SERVICE_ENVIRONMENT = 'service.environment'; +export const SERVICE_FRAMEWORK_NAME = 'service.framework.name'; +export const SERVICE_FRAMEWORK_VERSION = 'service.framework.version'; +export const SERVICE_LANGUAGE_NAME = 'service.language.name'; +export const SERVICE_LANGUAGE_VERSION = 'service.language.version'; +export const SERVICE_RUNTIME_NAME = 'service.runtime.name'; +export const SERVICE_RUNTIME_VERSION = 'service.runtime.version'; +export const SERVICE_NODE_NAME = 'service.node.name'; +export const SERVICE_VERSION = 'service.version'; +export const SERVICE_TARGET_TYPE = 'service.target.type'; +export const SERVICE_OVERFLOW_COUNT = 'service_transaction.aggregation.overflow_count'; + +export const URL_FULL = 'url.full'; +export const HTTP_REQUEST_METHOD = 'http.request.method'; +export const HTTP_RESPONSE_STATUS_CODE = 'http.response.status_code'; +export const USER_ID = 'user.id'; +export const USER_AGENT_ORIGINAL = 'user_agent.original'; +export const USER_AGENT_NAME = 'user_agent.name'; + +export const OBSERVER_HOSTNAME = 'observer.hostname'; +export const OBSERVER_LISTENING = 'observer.listening'; +export const PROCESSOR_EVENT = 'processor.event'; + +export const TRANSACTION_DURATION = 'transaction.duration.us'; +export const TRANSACTION_DURATION_HISTOGRAM = 'transaction.duration.histogram'; +export const TRANSACTION_DURATION_SUMMARY = 'transaction.duration.summary'; +export const TRANSACTION_TYPE = 'transaction.type'; +export const TRANSACTION_RESULT = 'transaction.result'; +export const TRANSACTION_NAME = 'transaction.name'; +export const TRANSACTION_ID = 'transaction.id'; +export const TRANSACTION_SAMPLED = 'transaction.sampled'; +export const TRANSACTION_PAGE_URL = 'transaction.page.url'; +export const TRANSACTION_FAILURE_COUNT = 'transaction.failure_count'; +export const TRANSACTION_SUCCESS_COUNT = 'transaction.success_count'; +export const TRANSACTION_OVERFLOW_COUNT = 'transaction.aggregation.overflow_count'; +// for transaction metrics +export const TRANSACTION_ROOT = 'transaction.root'; +export const TRANSACTION_PROFILER_STACK_TRACE_IDS = 'transaction.profiler_stack_trace_ids'; + +export const EVENT_OUTCOME = 'event.outcome'; + +export const TRACE_ID = 'trace.id'; + +export const SPAN_DURATION = 'span.duration.us'; +export const SPAN_TYPE = 'span.type'; +export const SPAN_SUBTYPE = 'span.subtype'; +export const SPAN_SELF_TIME_SUM = 'span.self_time.sum.us'; +export const SPAN_ACTION = 'span.action'; +export const SPAN_NAME = 'span.name'; +export const SPAN_ID = 'span.id'; +export const SPAN_DESTINATION_SERVICE_RESOURCE = 'span.destination.service.resource'; +export const SPAN_DESTINATION_SERVICE_RESPONSE_TIME_COUNT = + 'span.destination.service.response_time.count'; + +export const SPAN_DESTINATION_SERVICE_RESPONSE_TIME_SUM = + 'span.destination.service.response_time.sum.us'; + +export const SPAN_LINKS = 'span.links'; +export const SPAN_LINKS_TRACE_ID = 'span.links.trace.id'; +export const SPAN_LINKS_SPAN_ID = 'span.links.span.id'; + +export const SPAN_COMPOSITE_COUNT = 'span.composite.count'; +export const SPAN_COMPOSITE_SUM = 'span.composite.sum.us'; +export const SPAN_COMPOSITE_COMPRESSION_STRATEGY = 'span.composite.compression_strategy'; + +export const SPAN_SYNC = 'span.sync'; + +// Parent ID for a transaction or span +export const PARENT_ID = 'parent.id'; + +export const ERROR_ID = 'error.id'; +export const ERROR_GROUP_ID = 'error.grouping_key'; +export const ERROR_GROUP_NAME = 'error.grouping_name'; +export const ERROR_CULPRIT = 'error.culprit'; +export const ERROR_LOG_LEVEL = 'error.log.level'; +export const ERROR_LOG_MESSAGE = 'error.log.message'; +export const ERROR_EXCEPTION = 'error.exception'; +export const ERROR_EXC_MESSAGE = 'error.exception.message'; // only to be used in es queries, since error.exception is now an array +export const ERROR_EXC_HANDLED = 'error.exception.handled'; // only to be used in es queries, since error.exception is now an array +export const ERROR_EXC_TYPE = 'error.exception.type'; +export const ERROR_PAGE_URL = 'error.page.url'; +export const ERROR_TYPE = 'error.type'; + +// METRICS +export const METRIC_SYSTEM_FREE_MEMORY = 'system.memory.actual.free'; +export const METRIC_SYSTEM_TOTAL_MEMORY = 'system.memory.total'; +export const METRIC_SYSTEM_CPU_PERCENT = 'system.cpu.total.norm.pct'; +export const METRIC_PROCESS_CPU_PERCENT = 'system.process.cpu.total.norm.pct'; +export const METRIC_CGROUP_MEMORY_LIMIT_BYTES = 'system.process.cgroup.memory.mem.limit.bytes'; +export const METRIC_CGROUP_MEMORY_USAGE_BYTES = 'system.process.cgroup.memory.mem.usage.bytes'; + +export const METRIC_JAVA_HEAP_MEMORY_MAX = 'jvm.memory.heap.max'; +export const METRIC_JAVA_HEAP_MEMORY_COMMITTED = 'jvm.memory.heap.committed'; +export const METRIC_JAVA_HEAP_MEMORY_USED = 'jvm.memory.heap.used'; +export const METRIC_JAVA_NON_HEAP_MEMORY_MAX = 'jvm.memory.non_heap.max'; +export const METRIC_JAVA_NON_HEAP_MEMORY_COMMITTED = 'jvm.memory.non_heap.committed'; +export const METRIC_JAVA_NON_HEAP_MEMORY_USED = 'jvm.memory.non_heap.used'; +export const METRIC_JAVA_THREAD_COUNT = 'jvm.thread.count'; +export const METRIC_JAVA_GC_COUNT = 'jvm.gc.count'; +export const METRIC_JAVA_GC_TIME = 'jvm.gc.time'; + +export const METRICSET_NAME = 'metricset.name'; +export const METRICSET_INTERVAL = 'metricset.interval'; + +export const LABEL_NAME = 'labels.name'; +export const LABEL_GC = 'labels.gc'; +export const LABEL_TYPE = 'labels.type'; +export const LABEL_TELEMETRY_AUTO_VERSION = 'labels.telemetry_auto_version'; +export const LABEL_LIFECYCLE_STATE = 'labels.lifecycle_state'; + +export const HOST = 'host'; +export const HOST_HOSTNAME = 'host.hostname'; // Do not use. Please use `HOST_NAME` instead. +export const HOST_NAME = 'host.name'; +export const HOST_OS_PLATFORM = 'host.os.platform'; +export const HOST_ARCHITECTURE = 'host.architecture'; +export const HOST_OS_VERSION = 'host.os.version'; + +export const CONTAINER_ID = 'container.id'; +export const CONTAINER = 'container'; +export const CONTAINER_IMAGE = 'container.image.name'; + +export const KUBERNETES = 'kubernetes'; +export const KUBERNETES_POD_NAME = 'kubernetes.pod.name'; +export const KUBERNETES_POD_UID = 'kubernetes.pod.uid'; + +export const FAAS_ID = 'faas.id'; +export const FAAS_NAME = 'faas.name'; +export const FAAS_COLDSTART = 'faas.coldstart'; +export const FAAS_TRIGGER_TYPE = 'faas.trigger.type'; +export const FAAS_DURATION = 'faas.duration'; +export const FAAS_COLDSTART_DURATION = 'faas.coldstart_duration'; +export const FAAS_BILLED_DURATION = 'faas.billed_duration'; + +// OpenTelemetry Metrics +export const METRIC_OTEL_SYSTEM_CPU_UTILIZATION = 'system.cpu.utilization'; +export const METRIC_OTEL_SYSTEM_MEMORY_UTILIZATION = 'system.memory.utilization'; + +export const METRIC_OTEL_JVM_PROCESS_CPU_PERCENT = 'process.runtime.jvm.cpu.utilization'; +export const METRIC_OTEL_JVM_PROCESS_MEMORY_USAGE = 'process.runtime.jvm.memory.usage'; +export const METRIC_OTEL_JVM_PROCESS_MEMORY_COMMITTED = 'process.runtime.jvm.memory.committed'; +export const METRIC_OTEL_JVM_PROCESS_MEMORY_LIMIT = 'process.runtime.jvm.memory.limit'; +export const METRIC_OTEL_JVM_PROCESS_THREADS_COUNT = 'process.runtime.jvm.threads.count'; +export const METRIC_OTEL_JVM_SYSTEM_CPU_PERCENT = 'process.runtime.jvm.system.cpu.utilization'; +export const METRIC_OTEL_JVM_GC_DURATION = 'process.runtime.jvm.gc.duration'; +export const VALUE_OTEL_JVM_PROCESS_MEMORY_HEAP = 'heap'; +export const VALUE_OTEL_JVM_PROCESS_MEMORY_NON_HEAP = 'non_heap'; + +// Metadata +export const TIER = '_tier'; +export const INDEX = '_index'; +export const DATA_STEAM_TYPE = 'data_stream.type'; + +// Mobile +export const NETWORK_CONNECTION_TYPE = 'network.connection.type'; +export const DEVICE_MODEL_IDENTIFIER = 'device.model.identifier'; +export const SESSION_ID = 'session.id'; +export const APP_LAUNCH_TIME = 'application.launch.time'; +export const EVENT_NAME = 'event.name'; + +// Location +export const CLIENT_GEO_COUNTRY_ISO_CODE = 'client.geo.country_iso_code'; +export const CLIENT_GEO_REGION_ISO_CODE = 'client.geo.region_iso_code'; +export const CLIENT_GEO_COUNTRY_NAME = 'client.geo.country_name'; +export const CLIENT_GEO_CITY_NAME = 'client.geo.city_name'; +export const CLIENT_GEO_REGION_NAME = 'client.geo.region_name'; + +export const CHILD_ID = 'child.id'; diff --git a/packages/kbn-apm-types/src/es_schemas/raw/apm_base_doc.ts b/packages/kbn-apm-types/src/es_schemas/raw/apm_base_doc.ts new file mode 100644 index 0000000000000..a750c39c775d2 --- /dev/null +++ b/packages/kbn-apm-types/src/es_schemas/raw/apm_base_doc.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { Observer } from './fields/observer'; + +// all documents types extend APMBaseDoc and inherit all properties +export interface APMBaseDoc { + '@timestamp': string; + agent: { + name: string; + version: string; + }; + parent?: { id: string }; // parent ID is not available on root transactions + trace?: { id: string }; + labels?: { + [key: string]: string | number | boolean; + }; + observer?: Observer; +} diff --git a/packages/kbn-apm-types/src/es_schemas/raw/error_raw.ts b/packages/kbn-apm-types/src/es_schemas/raw/error_raw.ts new file mode 100644 index 0000000000000..f0157a6a08376 --- /dev/null +++ b/packages/kbn-apm-types/src/es_schemas/raw/error_raw.ts @@ -0,0 +1,75 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { APMBaseDoc } from './apm_base_doc'; +import { + Container, + Host, + Http, + Kubernetes, + Page, + Process, + Service, + Stackframe, + TimestampUs, + Url, + User, +} from './fields'; + +export interface Processor { + name: 'error'; + event: 'error'; +} + +export interface Exception { + attributes?: { + response?: string; + }; + code?: string; + message?: string; // either message or type are given + type?: string; + module?: string; + handled?: boolean; + stacktrace?: Stackframe[]; +} + +export interface Log { + message: string; + stacktrace?: Stackframe[]; +} + +export interface ErrorRaw extends APMBaseDoc { + processor: Processor; + timestamp: TimestampUs; + transaction?: { + id: string; + sampled?: boolean; + type: string; + }; + error: { + id: string; + culprit?: string; + grouping_key: string; + // either exception or log are given + exception?: Exception[]; + page?: Page; // special property for RUM: shared by error and transaction + log?: Log; + stack_trace?: string; + custom?: Record; + }; + + // Shared by errors and transactions + container?: Container; + host?: Host; + http?: Http; + kubernetes?: Kubernetes; + process?: Process; + service: Service; + url?: Url; + user?: User; +} diff --git a/packages/kbn-apm-types/src/es_schemas/raw/event_raw.ts b/packages/kbn-apm-types/src/es_schemas/raw/event_raw.ts new file mode 100644 index 0000000000000..f8d3124914239 --- /dev/null +++ b/packages/kbn-apm-types/src/es_schemas/raw/event_raw.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { APMBaseDoc } from './apm_base_doc'; +import { TimestampUs } from './fields/timestamp_us'; + +export interface EventRaw extends APMBaseDoc { + timestamp: TimestampUs; + transaction?: { + id: string; + sampled?: boolean; + type: string; + }; + log: { + message?: string; + }; + event: { + action: string; + category: string; + }; +} diff --git a/packages/kbn-apm-types/src/es_schemas/raw/fields/cloud.ts b/packages/kbn-apm-types/src/es_schemas/raw/fields/cloud.ts new file mode 100644 index 0000000000000..eaad379f5069b --- /dev/null +++ b/packages/kbn-apm-types/src/es_schemas/raw/fields/cloud.ts @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export interface Cloud { + availability_zone?: string; + instance?: { + name: string; + id: string; + }; + machine?: { + type: string; + }; + project?: { + id: string; + name: string; + }; + provider?: string; + region?: string; + account?: { + id: string; + name: string; + }; + image?: { + id: string; + }; + service?: { + name: string; + }; +} diff --git a/packages/kbn-apm-types/src/es_schemas/raw/fields/container.ts b/packages/kbn-apm-types/src/es_schemas/raw/fields/container.ts new file mode 100644 index 0000000000000..ae6526ad9ff92 --- /dev/null +++ b/packages/kbn-apm-types/src/es_schemas/raw/fields/container.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 + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export interface Container { + id?: string | null; + image?: string | null; +} diff --git a/packages/kbn-apm-types/src/es_schemas/raw/fields/event_outcome.ts b/packages/kbn-apm-types/src/es_schemas/raw/fields/event_outcome.ts new file mode 100644 index 0000000000000..53c19ab293d9b --- /dev/null +++ b/packages/kbn-apm-types/src/es_schemas/raw/fields/event_outcome.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export type EventOutcome = 'success' | 'failure' | 'unknown'; diff --git a/packages/kbn-apm-types/src/es_schemas/raw/fields/faas.ts b/packages/kbn-apm-types/src/es_schemas/raw/fields/faas.ts new file mode 100644 index 0000000000000..9054839b82902 --- /dev/null +++ b/packages/kbn-apm-types/src/es_schemas/raw/fields/faas.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export interface Faas { + id: string; + coldstart?: boolean; + execution?: string; + trigger?: { + type?: string; + request_id?: string; + }; +} diff --git a/packages/kbn-apm-types/src/es_schemas/raw/fields/host.ts b/packages/kbn-apm-types/src/es_schemas/raw/fields/host.ts new file mode 100644 index 0000000000000..7a12a8eba824f --- /dev/null +++ b/packages/kbn-apm-types/src/es_schemas/raw/fields/host.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export interface Host { + architecture?: string; + hostname?: string; + name?: string; + ip?: string; + os?: { + platform?: string; + }; +} diff --git a/packages/kbn-apm-types/src/es_schemas/raw/fields/http.ts b/packages/kbn-apm-types/src/es_schemas/raw/fields/http.ts new file mode 100644 index 0000000000000..3e16e5dceb80c --- /dev/null +++ b/packages/kbn-apm-types/src/es_schemas/raw/fields/http.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export interface Http { + request?: { method: string; [key: string]: unknown }; + response?: { status_code: number; [key: string]: unknown }; + version?: string; +} diff --git a/packages/kbn-apm-types/src/es_schemas/raw/fields/index.ts b/packages/kbn-apm-types/src/es_schemas/raw/fields/index.ts new file mode 100644 index 0000000000000..aa8355934fbb4 --- /dev/null +++ b/packages/kbn-apm-types/src/es_schemas/raw/fields/index.ts @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export * from './cloud'; +export * from './container'; +export * from './event_outcome'; +export * from './faas'; +export * from './host'; +export * from './http'; +export * from './kubernetes'; +export * from './observer'; +export * from './page'; +export * from './process'; +export * from './service'; +export * from './span_links'; +export * from './stackframe'; +export * from './timestamp_us'; +export * from './url'; +export * from './user_agent'; +export * from './user'; diff --git a/packages/kbn-apm-types/src/es_schemas/raw/fields/kubernetes.ts b/packages/kbn-apm-types/src/es_schemas/raw/fields/kubernetes.ts new file mode 100644 index 0000000000000..09667f08d441c --- /dev/null +++ b/packages/kbn-apm-types/src/es_schemas/raw/fields/kubernetes.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export interface Kubernetes { + pod?: { uid?: string | null; [key: string]: unknown }; + namespace?: string; + replicaset?: { + name?: string; + }; + deployment?: { + name?: string; + }; + container?: { + id?: string; + name?: string; + }; +} diff --git a/packages/kbn-apm-types/src/es_schemas/raw/fields/observer.ts b/packages/kbn-apm-types/src/es_schemas/raw/fields/observer.ts new file mode 100644 index 0000000000000..b035c0210bb35 --- /dev/null +++ b/packages/kbn-apm-types/src/es_schemas/raw/fields/observer.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export interface Observer { + ephemeral_id?: string; + hostname?: string; + id?: string; + name?: string; + type?: string; + version: string; + version_major: number; +} diff --git a/packages/kbn-apm-types/src/es_schemas/raw/fields/page.ts b/packages/kbn-apm-types/src/es_schemas/raw/fields/page.ts new file mode 100644 index 0000000000000..1c2548cb777cd --- /dev/null +++ b/packages/kbn-apm-types/src/es_schemas/raw/fields/page.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 + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +// only for RUM agent: shared by error and transaction +export interface Page { + url: string; +} diff --git a/packages/kbn-apm-types/src/es_schemas/raw/fields/process.ts b/packages/kbn-apm-types/src/es_schemas/raw/fields/process.ts new file mode 100644 index 0000000000000..25db0098f8d3d --- /dev/null +++ b/packages/kbn-apm-types/src/es_schemas/raw/fields/process.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export interface Process { + args?: string[]; + pid: number; + ppid?: number; + title?: string; +} diff --git a/packages/kbn-apm-types/src/es_schemas/raw/fields/service.ts b/packages/kbn-apm-types/src/es_schemas/raw/fields/service.ts new file mode 100644 index 0000000000000..ff01bfc8517e8 --- /dev/null +++ b/packages/kbn-apm-types/src/es_schemas/raw/fields/service.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export interface Service { + name: string; + environment?: string; + framework?: { + name: string; + version?: string; + }; + node?: { + name?: string; + }; + runtime?: { + name: string; + version: string; + }; + language?: { + name: string; + version?: string; + }; + version?: string; +} diff --git a/packages/kbn-apm-types/src/es_schemas/raw/fields/span_links.ts b/packages/kbn-apm-types/src/es_schemas/raw/fields/span_links.ts new file mode 100644 index 0000000000000..13ffc4d7075f1 --- /dev/null +++ b/packages/kbn-apm-types/src/es_schemas/raw/fields/span_links.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 + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export interface SpanLink { + trace: { id: string }; + span: { id: string }; +} diff --git a/packages/kbn-apm-types/src/es_schemas/raw/fields/stackframe.ts b/packages/kbn-apm-types/src/es_schemas/raw/fields/stackframe.ts new file mode 100644 index 0000000000000..b2b1cf8000103 --- /dev/null +++ b/packages/kbn-apm-types/src/es_schemas/raw/fields/stackframe.ts @@ -0,0 +1,44 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +interface Line { + column?: number; + number: number; +} + +interface Sourcemap { + error?: string; + updated?: boolean; +} + +interface StackframeBase { + abs_path?: string; + classname?: string; + context?: { + post?: string[]; + pre?: string[]; + }; + exclude_from_grouping?: boolean; + filename?: string; + function?: string; + module?: string; + library_frame?: boolean; + line?: Line; + sourcemap?: Sourcemap; + vars?: { + [key: string]: unknown; + }; +} + +export type StackframeWithLineContext = StackframeBase & { + line: Line & { + context: string; + }; +}; + +export type Stackframe = StackframeBase | StackframeWithLineContext; diff --git a/packages/kbn-apm-types/src/es_schemas/raw/fields/timestamp_us.ts b/packages/kbn-apm-types/src/es_schemas/raw/fields/timestamp_us.ts new file mode 100644 index 0000000000000..17c06c8e38156 --- /dev/null +++ b/packages/kbn-apm-types/src/es_schemas/raw/fields/timestamp_us.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export interface TimestampUs { + us: number; +} diff --git a/packages/kbn-apm-types/src/es_schemas/raw/fields/url.ts b/packages/kbn-apm-types/src/es_schemas/raw/fields/url.ts new file mode 100644 index 0000000000000..4fa149e6b65d6 --- /dev/null +++ b/packages/kbn-apm-types/src/es_schemas/raw/fields/url.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export interface Url { + domain?: string; + full: string; + original?: string; +} diff --git a/packages/kbn-apm-types/src/es_schemas/raw/fields/user.ts b/packages/kbn-apm-types/src/es_schemas/raw/fields/user.ts new file mode 100644 index 0000000000000..ced460ececd17 --- /dev/null +++ b/packages/kbn-apm-types/src/es_schemas/raw/fields/user.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export interface User { + id: string; +} diff --git a/packages/kbn-apm-types/src/es_schemas/raw/fields/user_agent.ts b/packages/kbn-apm-types/src/es_schemas/raw/fields/user_agent.ts new file mode 100644 index 0000000000000..0658d408dbe54 --- /dev/null +++ b/packages/kbn-apm-types/src/es_schemas/raw/fields/user_agent.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export interface UserAgent { + device?: { + name: string; + }; + name?: string; + original: string; + os?: { + name: string; + version?: string; + full?: string; + }; + version?: string; +} diff --git a/packages/kbn-apm-types/src/es_schemas/raw/index.ts b/packages/kbn-apm-types/src/es_schemas/raw/index.ts new file mode 100644 index 0000000000000..addd3279f2586 --- /dev/null +++ b/packages/kbn-apm-types/src/es_schemas/raw/index.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export * from './apm_base_doc'; +export * from './error_raw'; +export * from './event_raw'; +export * from './metric_raw'; +export * from './span_raw'; +export * from './transaction_raw'; diff --git a/packages/kbn-apm-types/src/es_schemas/raw/metric_raw.ts b/packages/kbn-apm-types/src/es_schemas/raw/metric_raw.ts new file mode 100644 index 0000000000000..29a8dc921f3d9 --- /dev/null +++ b/packages/kbn-apm-types/src/es_schemas/raw/metric_raw.ts @@ -0,0 +1,124 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { APMBaseDoc } from './apm_base_doc'; +import { Cloud } from './fields/cloud'; +import { Container } from './fields/container'; +import { Host } from './fields/host'; +import { Kubernetes } from './fields/kubernetes'; +import { Service } from './fields/service'; + +type BaseMetric = APMBaseDoc & { + processor: { + name: 'metric'; + event: 'metric'; + }; + cloud?: Cloud; + container?: Container; + kubernetes?: Kubernetes; + service?: Service; + host?: Host; +}; + +type BaseBreakdownMetric = BaseMetric & { + transaction: { + name: string; + type: string; + }; + span: { + self_time: { + count: number; + sum: { + us: number; + }; + }; + }; +}; + +type TransactionBreakdownMetric = BaseBreakdownMetric & { + transaction: { + duration: { + count: number; + sum: { + us: number; + }; + }; + breakdown: { + count: number; + }; + }; +}; + +type SpanBreakdownMetric = BaseBreakdownMetric & { + span: { + type: string; + subtype?: string; + }; +}; + +type SystemMetric = BaseMetric & { + system: unknown; + service: { + node?: { + name: string; + }; + }; +}; + +type CGroupMetric = SystemMetric; +type JVMMetric = SystemMetric & { + jvm: unknown; +}; + +type TransactionDurationMetric = BaseMetric & { + transaction: { + name: string; + type: string; + result?: string; + duration: { + histogram: { + values: number[]; + counts: number[]; + }; + }; + }; + service: { + name: string; + node?: { + name: string; + }; + environment?: string; + version?: string; + }; +}; + +export type SpanDestinationMetric = BaseMetric & { + span: { + destination: { + service: { + resource: string; + response_time: { + count: number; + sum: { + us: number; + }; + }; + }; + }; + }; +}; + +export type MetricRaw = + | BaseMetric + | TransactionBreakdownMetric + | SpanBreakdownMetric + | TransactionDurationMetric + | SpanDestinationMetric + | SystemMetric + | CGroupMetric + | JVMMetric; diff --git a/packages/kbn-apm-types/src/es_schemas/raw/span_raw.ts b/packages/kbn-apm-types/src/es_schemas/raw/span_raw.ts new file mode 100644 index 0000000000000..0d45d3bb00cc4 --- /dev/null +++ b/packages/kbn-apm-types/src/es_schemas/raw/span_raw.ts @@ -0,0 +1,80 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { APMBaseDoc } from './apm_base_doc'; +import { EventOutcome } from './fields/event_outcome'; +import { Http } from './fields/http'; +import { SpanLink } from './fields/span_links'; +import { Stackframe } from './fields/stackframe'; +import { TimestampUs } from './fields/timestamp_us'; +import { Url } from './fields/url'; + +interface Processor { + name: 'transaction'; + event: 'span'; +} + +export interface SpanRaw extends APMBaseDoc { + processor: Processor; + trace: { id: string }; // trace is required + event?: { outcome?: EventOutcome }; + service: { + name: string; + environment?: string; + }; + span: { + destination?: { + service: { + resource: string; + }; + }; + action?: string; + duration: { us: number }; + id: string; + name: string; + stacktrace?: Stackframe[]; + subtype?: string; + sync?: boolean; + type: string; + http?: { + url?: { + original?: string; + }; + response: { + status_code: number; + }; + method?: string; + }; + db?: { + statement?: string; + type?: string; + }; + message?: { + queue?: { name: string }; + age?: { ms: number }; + body?: string; + headers?: Record; + }; + composite?: { + count: number; + sum: { us: number }; + compression_strategy: string; + }; + links?: SpanLink[]; + }; + timestamp: TimestampUs; + transaction?: { + id: string; + }; + child?: { id: string[] }; + code?: { + stacktrace?: string; + }; + http?: Http; + url?: Url; +} diff --git a/packages/kbn-apm-types/src/es_schemas/raw/transaction_raw.ts b/packages/kbn-apm-types/src/es_schemas/raw/transaction_raw.ts new file mode 100644 index 0000000000000..6505e2808f795 --- /dev/null +++ b/packages/kbn-apm-types/src/es_schemas/raw/transaction_raw.ts @@ -0,0 +1,79 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { APMBaseDoc } from './apm_base_doc'; +import { Cloud } from './fields/cloud'; +import { Container } from './fields/container'; +import { EventOutcome } from './fields/event_outcome'; +import { Host } from './fields/host'; +import { Http } from './fields/http'; +import { Kubernetes } from './fields/kubernetes'; +import { Page } from './fields/page'; +import { Process } from './fields/process'; +import { Service } from './fields/service'; +import { TimestampUs } from './fields/timestamp_us'; +import { Url } from './fields/url'; +import { User } from './fields/user'; +import { UserAgent } from './fields/user_agent'; +import { Faas } from './fields/faas'; +import { SpanLink } from './fields/span_links'; + +interface Processor { + name: 'transaction'; + event: 'transaction'; +} + +export interface TransactionRaw extends APMBaseDoc { + processor: Processor; + timestamp: TimestampUs; + trace: { id: string }; // trace is required + event?: { outcome?: EventOutcome }; + transaction: { + duration: { us: number }; + id: string; + marks?: { + // "agent": not defined by APM Server - only sent by RUM agent + agent?: { + [name: string]: number; + }; + }; + name?: string; + page?: Page; // special property for RUM: shared by error and transaction + result?: string; + sampled: boolean; + span_count?: { + started?: number; + dropped?: number; + }; + type: string; + custom?: Record; + message?: { + queue?: { name: string }; + age?: { ms: number }; + body?: string; + headers?: Record; + }; + }; + + // Shared by errors and transactions + container?: Container; + ecs?: { version?: string }; + host?: Host; + http?: Http; + kubernetes?: Kubernetes; + process?: Process; + service: Service; + url?: Url; + user?: User; + user_agent?: UserAgent; + cloud?: Cloud; + faas?: Faas; + span?: { + links?: SpanLink[]; + }; +} diff --git a/packages/kbn-apm-types/src/es_schemas/ui/apm_error.ts b/packages/kbn-apm-types/src/es_schemas/ui/apm_error.ts new file mode 100644 index 0000000000000..fad4190a229ef --- /dev/null +++ b/packages/kbn-apm-types/src/es_schemas/ui/apm_error.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { ErrorRaw } from '../raw/error_raw'; +import { Agent } from './fields/agent'; + +export interface APMError extends ErrorRaw { + agent: Agent; +} diff --git a/packages/kbn-apm-types/src/es_schemas/ui/event.ts b/packages/kbn-apm-types/src/es_schemas/ui/event.ts new file mode 100644 index 0000000000000..8d0fd78f5f0f9 --- /dev/null +++ b/packages/kbn-apm-types/src/es_schemas/ui/event.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { EventRaw } from '../raw/event_raw'; +import { Agent } from './fields/agent'; + +export interface Event extends EventRaw { + agent: Agent; +} diff --git a/packages/kbn-apm-types/src/es_schemas/ui/fields/agent.ts b/packages/kbn-apm-types/src/es_schemas/ui/fields/agent.ts new file mode 100644 index 0000000000000..85cb7340fdda6 --- /dev/null +++ b/packages/kbn-apm-types/src/es_schemas/ui/fields/agent.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { AgentName } from '@kbn/elastic-agent-utils'; + +export type { ElasticAgentName, OpenTelemetryAgentName, AgentName } from '@kbn/elastic-agent-utils'; + +export interface Agent { + ephemeral_id?: string; + name: AgentName; + version: string; +} diff --git a/packages/kbn-apm-types/src/es_schemas/ui/fields/index.ts b/packages/kbn-apm-types/src/es_schemas/ui/fields/index.ts new file mode 100644 index 0000000000000..1e64e14e07f8a --- /dev/null +++ b/packages/kbn-apm-types/src/es_schemas/ui/fields/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +export * from './agent'; diff --git a/packages/kbn-apm-types/src/es_schemas/ui/index.ts b/packages/kbn-apm-types/src/es_schemas/ui/index.ts new file mode 100644 index 0000000000000..26f716289aaff --- /dev/null +++ b/packages/kbn-apm-types/src/es_schemas/ui/index.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export * from './apm_error'; +export * from './event'; +export * from './metric'; +export * from './span'; +export * from './transaction'; diff --git a/packages/kbn-apm-types/src/es_schemas/ui/metric.ts b/packages/kbn-apm-types/src/es_schemas/ui/metric.ts new file mode 100644 index 0000000000000..bd9391e269554 --- /dev/null +++ b/packages/kbn-apm-types/src/es_schemas/ui/metric.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { MetricRaw } from '../raw/metric_raw'; + +export type Metric = MetricRaw; diff --git a/packages/kbn-apm-types/src/es_schemas/ui/span.ts b/packages/kbn-apm-types/src/es_schemas/ui/span.ts new file mode 100644 index 0000000000000..5e09b84b87df2 --- /dev/null +++ b/packages/kbn-apm-types/src/es_schemas/ui/span.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { SpanRaw } from '../raw/span_raw'; +import { Agent } from './fields/agent'; + +export interface Span extends SpanRaw { + agent: Agent; +} diff --git a/packages/kbn-apm-types/src/es_schemas/ui/transaction.ts b/packages/kbn-apm-types/src/es_schemas/ui/transaction.ts new file mode 100644 index 0000000000000..ea5ccf5fd6434 --- /dev/null +++ b/packages/kbn-apm-types/src/es_schemas/ui/transaction.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { TransactionRaw } from '../raw/transaction_raw'; +import { Agent } from './fields/agent'; + +// Make `transaction.name` required instead of optional. +// `transaction.name` can be missing in Elasticsearch but the UI will only aggregate on transactions with a name, +// and thus it doesn't make sense to treat it as optional +type InnerTransaction = TransactionRaw['transaction']; +interface InnerTransactionWithName extends InnerTransaction { + name: string; +} + +export interface Transaction extends TransactionRaw { + agent: Agent; + transaction: InnerTransactionWithName; +} diff --git a/packages/kbn-apm-types/tsconfig.json b/packages/kbn-apm-types/tsconfig.json new file mode 100644 index 0000000000000..312ebd695e48e --- /dev/null +++ b/packages/kbn-apm-types/tsconfig.json @@ -0,0 +1,18 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": [ + "node" + ] + }, + "include": [ + "**/*.ts" + ], + "exclude": [ + "target/**/*", + ], + "kbn_references": [ + "@kbn/elastic-agent-utils", + ] +} diff --git a/tsconfig.base.json b/tsconfig.base.json index 18fca49a07497..1d8a9b6c83833 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -86,6 +86,8 @@ "@kbn/apm-synthtrace/*": ["packages/kbn-apm-synthtrace/*"], "@kbn/apm-synthtrace-client": ["packages/kbn-apm-synthtrace-client"], "@kbn/apm-synthtrace-client/*": ["packages/kbn-apm-synthtrace-client/*"], + "@kbn/apm-types": ["packages/kbn-apm-types"], + "@kbn/apm-types/*": ["packages/kbn-apm-types/*"], "@kbn/apm-utils": ["packages/kbn-apm-utils"], "@kbn/apm-utils/*": ["packages/kbn-apm-utils/*"], "@kbn/app-link-test-plugin": ["test/plugin_functional/plugins/app_link_test"], diff --git a/x-pack/plugins/observability_solution/apm/common/es_fields/apm.ts b/x-pack/plugins/observability_solution/apm/common/es_fields/apm.ts index 539484fed182e..9ab614df857fe 100644 --- a/x-pack/plugins/observability_solution/apm/common/es_fields/apm.ts +++ b/x-pack/plugins/observability_solution/apm/common/es_fields/apm.ts @@ -4,192 +4,5 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -export const TIMESTAMP = 'timestamp.us'; -export const AGENT = 'agent'; -export const AGENT_NAME = 'agent.name'; -export const AGENT_VERSION = 'agent.version'; -export const AGENT_ACTIVATION_METHOD = 'agent.activation_method'; -export const DESTINATION_ADDRESS = 'destination.address'; - -export const CLOUD = 'cloud'; -export const CLOUD_AVAILABILITY_ZONE = 'cloud.availability_zone'; -export const CLOUD_PROVIDER = 'cloud.provider'; -export const CLOUD_REGION = 'cloud.region'; -export const CLOUD_MACHINE_TYPE = 'cloud.machine.type'; -export const CLOUD_ACCOUNT_ID = 'cloud.account.id'; -export const CLOUD_INSTANCE_ID = 'cloud.instance.id'; -export const CLOUD_INSTANCE_NAME = 'cloud.instance.name'; -export const CLOUD_SERVICE_NAME = 'cloud.service.name'; - -export const EVENT_SUCCESS_COUNT = 'event.success_count'; - -export const SERVICE = 'service'; -export const SERVICE_NAME = 'service.name'; -export const SERVICE_ENVIRONMENT = 'service.environment'; -export const SERVICE_FRAMEWORK_NAME = 'service.framework.name'; -export const SERVICE_FRAMEWORK_VERSION = 'service.framework.version'; -export const SERVICE_LANGUAGE_NAME = 'service.language.name'; -export const SERVICE_LANGUAGE_VERSION = 'service.language.version'; -export const SERVICE_RUNTIME_NAME = 'service.runtime.name'; -export const SERVICE_RUNTIME_VERSION = 'service.runtime.version'; -export const SERVICE_NODE_NAME = 'service.node.name'; -export const SERVICE_VERSION = 'service.version'; -export const SERVICE_TARGET_TYPE = 'service.target.type'; -export const SERVICE_OVERFLOW_COUNT = 'service_transaction.aggregation.overflow_count'; - -export const URL_FULL = 'url.full'; -export const HTTP_REQUEST_METHOD = 'http.request.method'; -export const HTTP_RESPONSE_STATUS_CODE = 'http.response.status_code'; -export const USER_ID = 'user.id'; -export const USER_AGENT_ORIGINAL = 'user_agent.original'; -export const USER_AGENT_NAME = 'user_agent.name'; - -export const OBSERVER_HOSTNAME = 'observer.hostname'; -export const OBSERVER_LISTENING = 'observer.listening'; -export const PROCESSOR_EVENT = 'processor.event'; - -export const TRANSACTION_DURATION = 'transaction.duration.us'; -export const TRANSACTION_DURATION_HISTOGRAM = 'transaction.duration.histogram'; -export const TRANSACTION_DURATION_SUMMARY = 'transaction.duration.summary'; -export const TRANSACTION_TYPE = 'transaction.type'; -export const TRANSACTION_RESULT = 'transaction.result'; -export const TRANSACTION_NAME = 'transaction.name'; -export const TRANSACTION_ID = 'transaction.id'; -export const TRANSACTION_SAMPLED = 'transaction.sampled'; -export const TRANSACTION_PAGE_URL = 'transaction.page.url'; -export const TRANSACTION_FAILURE_COUNT = 'transaction.failure_count'; -export const TRANSACTION_SUCCESS_COUNT = 'transaction.success_count'; -export const TRANSACTION_OVERFLOW_COUNT = 'transaction.aggregation.overflow_count'; -// for transaction metrics -export const TRANSACTION_ROOT = 'transaction.root'; -export const TRANSACTION_PROFILER_STACK_TRACE_IDS = 'transaction.profiler_stack_trace_ids'; - -export const EVENT_OUTCOME = 'event.outcome'; - -export const TRACE_ID = 'trace.id'; - -export const SPAN_DURATION = 'span.duration.us'; -export const SPAN_TYPE = 'span.type'; -export const SPAN_SUBTYPE = 'span.subtype'; -export const SPAN_SELF_TIME_SUM = 'span.self_time.sum.us'; -export const SPAN_ACTION = 'span.action'; -export const SPAN_NAME = 'span.name'; -export const SPAN_ID = 'span.id'; -export const SPAN_DESTINATION_SERVICE_RESOURCE = 'span.destination.service.resource'; -export const SPAN_DESTINATION_SERVICE_RESPONSE_TIME_COUNT = - 'span.destination.service.response_time.count'; - -export const SPAN_DESTINATION_SERVICE_RESPONSE_TIME_SUM = - 'span.destination.service.response_time.sum.us'; - -export const SPAN_LINKS = 'span.links'; -export const SPAN_LINKS_TRACE_ID = 'span.links.trace.id'; -export const SPAN_LINKS_SPAN_ID = 'span.links.span.id'; - -export const SPAN_COMPOSITE_COUNT = 'span.composite.count'; -export const SPAN_COMPOSITE_SUM = 'span.composite.sum.us'; -export const SPAN_COMPOSITE_COMPRESSION_STRATEGY = 'span.composite.compression_strategy'; - -export const SPAN_SYNC = 'span.sync'; - -// Parent ID for a transaction or span -export const PARENT_ID = 'parent.id'; - -export const ERROR_ID = 'error.id'; -export const ERROR_GROUP_ID = 'error.grouping_key'; -export const ERROR_GROUP_NAME = 'error.grouping_name'; -export const ERROR_CULPRIT = 'error.culprit'; -export const ERROR_LOG_LEVEL = 'error.log.level'; -export const ERROR_LOG_MESSAGE = 'error.log.message'; -export const ERROR_EXCEPTION = 'error.exception'; -export const ERROR_EXC_MESSAGE = 'error.exception.message'; // only to be used in es queries, since error.exception is now an array -export const ERROR_EXC_HANDLED = 'error.exception.handled'; // only to be used in es queries, since error.exception is now an array -export const ERROR_EXC_TYPE = 'error.exception.type'; -export const ERROR_PAGE_URL = 'error.page.url'; -export const ERROR_TYPE = 'error.type'; - -// METRICS -export const METRIC_SYSTEM_FREE_MEMORY = 'system.memory.actual.free'; -export const METRIC_SYSTEM_TOTAL_MEMORY = 'system.memory.total'; -export const METRIC_SYSTEM_CPU_PERCENT = 'system.cpu.total.norm.pct'; -export const METRIC_PROCESS_CPU_PERCENT = 'system.process.cpu.total.norm.pct'; -export const METRIC_CGROUP_MEMORY_LIMIT_BYTES = 'system.process.cgroup.memory.mem.limit.bytes'; -export const METRIC_CGROUP_MEMORY_USAGE_BYTES = 'system.process.cgroup.memory.mem.usage.bytes'; - -export const METRIC_JAVA_HEAP_MEMORY_MAX = 'jvm.memory.heap.max'; -export const METRIC_JAVA_HEAP_MEMORY_COMMITTED = 'jvm.memory.heap.committed'; -export const METRIC_JAVA_HEAP_MEMORY_USED = 'jvm.memory.heap.used'; -export const METRIC_JAVA_NON_HEAP_MEMORY_MAX = 'jvm.memory.non_heap.max'; -export const METRIC_JAVA_NON_HEAP_MEMORY_COMMITTED = 'jvm.memory.non_heap.committed'; -export const METRIC_JAVA_NON_HEAP_MEMORY_USED = 'jvm.memory.non_heap.used'; -export const METRIC_JAVA_THREAD_COUNT = 'jvm.thread.count'; -export const METRIC_JAVA_GC_COUNT = 'jvm.gc.count'; -export const METRIC_JAVA_GC_TIME = 'jvm.gc.time'; - -export const METRICSET_NAME = 'metricset.name'; -export const METRICSET_INTERVAL = 'metricset.interval'; - -export const LABEL_NAME = 'labels.name'; -export const LABEL_GC = 'labels.gc'; -export const LABEL_TYPE = 'labels.type'; -export const LABEL_TELEMETRY_AUTO_VERSION = 'labels.telemetry_auto_version'; -export const LABEL_LIFECYCLE_STATE = 'labels.lifecycle_state'; - -export const HOST = 'host'; -export const HOST_HOSTNAME = 'host.hostname'; // Do not use. Please use `HOST_NAME` instead. -export const HOST_NAME = 'host.name'; -export const HOST_OS_PLATFORM = 'host.os.platform'; -export const HOST_ARCHITECTURE = 'host.architecture'; -export const HOST_OS_VERSION = 'host.os.version'; - -export const CONTAINER_ID = 'container.id'; -export const CONTAINER = 'container'; -export const CONTAINER_IMAGE = 'container.image.name'; - -export const KUBERNETES = 'kubernetes'; -export const KUBERNETES_POD_NAME = 'kubernetes.pod.name'; -export const KUBERNETES_POD_UID = 'kubernetes.pod.uid'; - -export const FAAS_ID = 'faas.id'; -export const FAAS_NAME = 'faas.name'; -export const FAAS_COLDSTART = 'faas.coldstart'; -export const FAAS_TRIGGER_TYPE = 'faas.trigger.type'; -export const FAAS_DURATION = 'faas.duration'; -export const FAAS_COLDSTART_DURATION = 'faas.coldstart_duration'; -export const FAAS_BILLED_DURATION = 'faas.billed_duration'; - -// OpenTelemetry Metrics -export const METRIC_OTEL_SYSTEM_CPU_UTILIZATION = 'system.cpu.utilization'; -export const METRIC_OTEL_SYSTEM_MEMORY_UTILIZATION = 'system.memory.utilization'; - -export const METRIC_OTEL_JVM_PROCESS_CPU_PERCENT = 'process.runtime.jvm.cpu.utilization'; -export const METRIC_OTEL_JVM_PROCESS_MEMORY_USAGE = 'process.runtime.jvm.memory.usage'; -export const METRIC_OTEL_JVM_PROCESS_MEMORY_COMMITTED = 'process.runtime.jvm.memory.committed'; -export const METRIC_OTEL_JVM_PROCESS_MEMORY_LIMIT = 'process.runtime.jvm.memory.limit'; -export const METRIC_OTEL_JVM_PROCESS_THREADS_COUNT = 'process.runtime.jvm.threads.count'; -export const METRIC_OTEL_JVM_SYSTEM_CPU_PERCENT = 'process.runtime.jvm.system.cpu.utilization'; -export const METRIC_OTEL_JVM_GC_DURATION = 'process.runtime.jvm.gc.duration'; -export const VALUE_OTEL_JVM_PROCESS_MEMORY_HEAP = 'heap'; -export const VALUE_OTEL_JVM_PROCESS_MEMORY_NON_HEAP = 'non_heap'; - -// Metadata -export const TIER = '_tier'; -export const INDEX = '_index'; -export const DATA_STEAM_TYPE = 'data_stream.type'; - -// Mobile -export const NETWORK_CONNECTION_TYPE = 'network.connection.type'; -export const DEVICE_MODEL_IDENTIFIER = 'device.model.identifier'; -export const SESSION_ID = 'session.id'; -export const APP_LAUNCH_TIME = 'application.launch.time'; -export const EVENT_NAME = 'event.name'; - -// Location -export const CLIENT_GEO_COUNTRY_ISO_CODE = 'client.geo.country_iso_code'; -export const CLIENT_GEO_REGION_ISO_CODE = 'client.geo.region_iso_code'; -export const CLIENT_GEO_COUNTRY_NAME = 'client.geo.country_name'; -export const CLIENT_GEO_CITY_NAME = 'client.geo.city_name'; -export const CLIENT_GEO_REGION_NAME = 'client.geo.region_name'; - -export const CHILD_ID = 'child.id'; +export * from '@kbn/apm-types/es_fields'; diff --git a/x-pack/plugins/observability_solution/apm/tsconfig.json b/x-pack/plugins/observability_solution/apm/tsconfig.json index f9db8e36fd63d..fcc6346802830 100644 --- a/x-pack/plugins/observability_solution/apm/tsconfig.json +++ b/x-pack/plugins/observability_solution/apm/tsconfig.json @@ -127,7 +127,8 @@ "@kbn/shared-ux-page-no-data-config-types", "@kbn/react-hooks", "@kbn/server-route-repository-utils", - "@kbn/core-analytics-browser" + "@kbn/core-analytics-browser", + "@kbn/apm-types" ], "exclude": [ "target/**/*" diff --git a/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/apm_base_doc.ts b/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/apm_base_doc.ts index e39d90ff58b99..ffe2c708b2e59 100644 --- a/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/apm_base_doc.ts +++ b/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/apm_base_doc.ts @@ -5,19 +5,4 @@ * 2.0. */ -import { Observer } from './fields/observer'; - -// all documents types extend APMBaseDoc and inherit all properties -export interface APMBaseDoc { - '@timestamp': string; - agent: { - name: string; - version: string; - }; - parent?: { id: string }; // parent ID is not available on root transactions - trace?: { id: string }; - labels?: { - [key: string]: string | number | boolean; - }; - observer?: Observer; -} +export type { APMBaseDoc } from '@kbn/apm-types/es_schemas_raw'; diff --git a/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/error_raw.ts b/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/error_raw.ts index 34e24033db230..f86fb663dac4f 100644 --- a/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/error_raw.ts +++ b/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/error_raw.ts @@ -5,68 +5,4 @@ * 2.0. */ -import { APMBaseDoc } from './apm_base_doc'; -import { Container } from './fields/container'; -import { Host } from './fields/host'; -import { Http } from './fields/http'; -import { Kubernetes } from './fields/kubernetes'; -import { Page } from './fields/page'; -import { Process } from './fields/process'; -import { Service } from './fields/service'; -import { Stackframe } from './fields/stackframe'; -import { TimestampUs } from './fields/timestamp_us'; -import { Url } from './fields/url'; -import { User } from './fields/user'; - -export interface Processor { - name: 'error'; - event: 'error'; -} - -export interface Exception { - attributes?: { - response?: string; - }; - code?: string; - message?: string; // either message or type are given - type?: string; - module?: string; - handled?: boolean; - stacktrace?: Stackframe[]; -} - -export interface Log { - message: string; - stacktrace?: Stackframe[]; -} - -export interface ErrorRaw extends APMBaseDoc { - processor: Processor; - timestamp: TimestampUs; - transaction?: { - id: string; - sampled?: boolean; - type: string; - }; - error: { - id: string; - culprit?: string; - grouping_key: string; - // either exception or log are given - exception?: Exception[]; - page?: Page; // special property for RUM: shared by error and transaction - log?: Log; - stack_trace?: string; - custom?: Record; - }; - - // Shared by errors and transactions - container?: Container; - host?: Host; - http?: Http; - kubernetes?: Kubernetes; - process?: Process; - service: Service; - url?: Url; - user?: User; -} +export type { ErrorRaw, Log, Exception, Processor } from '@kbn/apm-types/es_schemas_raw'; diff --git a/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/event_raw.ts b/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/event_raw.ts index 31a1952cdc03d..b0e4f134164bc 100644 --- a/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/event_raw.ts +++ b/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/event_raw.ts @@ -5,21 +5,4 @@ * 2.0. */ -import { APMBaseDoc } from './apm_base_doc'; -import { TimestampUs } from './fields/timestamp_us'; - -export interface EventRaw extends APMBaseDoc { - timestamp: TimestampUs; - transaction?: { - id: string; - sampled?: boolean; - type: string; - }; - log: { - message?: string; - }; - event: { - action: string; - category: string; - }; -} +export type { EventRaw } from '@kbn/apm-types/es_schemas_raw'; diff --git a/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/cloud.ts b/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/cloud.ts index bc0c3ea8002ad..b187506ef52d4 100644 --- a/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/cloud.ts +++ b/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/cloud.ts @@ -5,29 +5,4 @@ * 2.0. */ -export interface Cloud { - availability_zone?: string; - instance?: { - name: string; - id: string; - }; - machine?: { - type: string; - }; - project?: { - id: string; - name: string; - }; - provider?: string; - region?: string; - account?: { - id: string; - name: string; - }; - image?: { - id: string; - }; - service?: { - name: string; - }; -} +export type { Cloud } from '@kbn/apm-types/es_schemas_raw'; diff --git a/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/container.ts b/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/container.ts index c17517b7c5f2d..fc5f749f46f60 100644 --- a/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/container.ts +++ b/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/container.ts @@ -5,7 +5,4 @@ * 2.0. */ -export interface Container { - id?: string | null; - image?: string | null; -} +export type { Container } from '@kbn/apm-types/es_schemas_raw'; diff --git a/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/event_outcome.ts b/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/event_outcome.ts index d0ca41fcba4ed..8ec05dc0274ee 100644 --- a/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/event_outcome.ts +++ b/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/event_outcome.ts @@ -5,4 +5,4 @@ * 2.0. */ -export type EventOutcome = 'success' | 'failure' | 'unknown'; +export type { EventOutcome } from '@kbn/apm-types/es_schemas_raw'; diff --git a/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/faas.ts b/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/faas.ts index 1229b8134ac13..1779fc3528132 100644 --- a/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/faas.ts +++ b/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/faas.ts @@ -5,12 +5,4 @@ * 2.0. */ -export interface Faas { - id: string; - coldstart?: boolean; - execution?: string; - trigger?: { - type?: string; - request_id?: string; - }; -} +export type { Faas } from '@kbn/apm-types/es_schemas_raw'; diff --git a/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/host.ts b/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/host.ts index 6d1941ff0184c..96cf79ab1d659 100644 --- a/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/host.ts +++ b/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/host.ts @@ -5,12 +5,4 @@ * 2.0. */ -export interface Host { - architecture?: string; - hostname?: string; - name?: string; - ip?: string; - os?: { - platform?: string; - }; -} +export type { Host } from '@kbn/apm-types/es_schemas_raw'; diff --git a/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/http.ts b/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/http.ts index 547f117b41326..b2f7ffc6843ef 100644 --- a/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/http.ts +++ b/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/http.ts @@ -5,8 +5,4 @@ * 2.0. */ -export interface Http { - request?: { method: string; [key: string]: unknown }; - response?: { status_code: number; [key: string]: unknown }; - version?: string; -} +export type { Http } from '@kbn/apm-types/es_schemas_raw'; diff --git a/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/kubernetes.ts b/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/kubernetes.ts index 5cf0b497dad18..4c7cbf15dd2b2 100644 --- a/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/kubernetes.ts +++ b/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/kubernetes.ts @@ -5,17 +5,4 @@ * 2.0. */ -export interface Kubernetes { - pod?: { uid?: string | null; [key: string]: unknown }; - namespace?: string; - replicaset?: { - name?: string; - }; - deployment?: { - name?: string; - }; - container?: { - id?: string; - name?: string; - }; -} +export type { Kubernetes } from '@kbn/apm-types/es_schemas_raw'; diff --git a/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/observer.ts b/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/observer.ts index 81a0bf1e0bfd2..65fab1814a172 100644 --- a/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/observer.ts +++ b/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/observer.ts @@ -5,12 +5,4 @@ * 2.0. */ -export interface Observer { - ephemeral_id?: string; - hostname?: string; - id?: string; - name?: string; - type?: string; - version: string; - version_major: number; -} +export type { Observer } from '@kbn/apm-types/es_schemas_raw'; diff --git a/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/page.ts b/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/page.ts index f934435594786..3070d8ab6ec69 100644 --- a/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/page.ts +++ b/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/page.ts @@ -6,6 +6,4 @@ */ // only for RUM agent: shared by error and transaction -export interface Page { - url: string; -} +export type { Page } from '@kbn/apm-types/es_schemas_raw'; diff --git a/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/process.ts b/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/process.ts index 10973e3b66a5f..c372c6f3ef959 100644 --- a/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/process.ts +++ b/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/process.ts @@ -5,9 +5,4 @@ * 2.0. */ -export interface Process { - args?: string[]; - pid: number; - ppid?: number; - title?: string; -} +export type { Process } from '@kbn/apm-types/es_schemas_raw'; diff --git a/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/service.ts b/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/service.ts index 7158c886e8109..dc7f872117cdc 100644 --- a/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/service.ts +++ b/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/service.ts @@ -5,23 +5,4 @@ * 2.0. */ -export interface Service { - name: string; - environment?: string; - framework?: { - name: string; - version?: string; - }; - node?: { - name?: string; - }; - runtime?: { - name: string; - version: string; - }; - language?: { - name: string; - version?: string; - }; - version?: string; -} +export type { Service } from '@kbn/apm-types/es_schemas_raw'; diff --git a/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/span_links.ts b/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/span_links.ts index 5e0028ad58176..05027bfd70bf5 100644 --- a/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/span_links.ts +++ b/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/span_links.ts @@ -5,7 +5,4 @@ * 2.0. */ -export interface SpanLink { - trace: { id: string }; - span: { id: string }; -} +export type { SpanLink } from '@kbn/apm-types/es_schemas_raw'; diff --git a/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/stackframe.ts b/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/stackframe.ts index 90d7f20047573..ca357ec00732d 100644 --- a/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/stackframe.ts +++ b/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/stackframe.ts @@ -5,39 +5,4 @@ * 2.0. */ -interface Line { - column?: number; - number: number; -} - -interface Sourcemap { - error?: string; - updated?: boolean; -} - -interface StackframeBase { - abs_path?: string; - classname?: string; - context?: { - post?: string[]; - pre?: string[]; - }; - exclude_from_grouping?: boolean; - filename?: string; - function?: string; - module?: string; - library_frame?: boolean; - line?: Line; - sourcemap?: Sourcemap; - vars?: { - [key: string]: unknown; - }; -} - -export type StackframeWithLineContext = StackframeBase & { - line: Line & { - context: string; - }; -}; - -export type Stackframe = StackframeBase | StackframeWithLineContext; +export type { StackframeWithLineContext, Stackframe } from '@kbn/apm-types/es_schemas_raw'; diff --git a/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/timestamp_us.ts b/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/timestamp_us.ts index f6f944b6fe95f..a36b28a35635f 100644 --- a/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/timestamp_us.ts +++ b/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/timestamp_us.ts @@ -5,6 +5,4 @@ * 2.0. */ -export interface TimestampUs { - us: number; -} +export type { TimestampUs } from '@kbn/apm-types/es_schemas_raw'; diff --git a/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/url.ts b/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/url.ts index 001d6370e5f06..f30ba85fd474d 100644 --- a/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/url.ts +++ b/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/url.ts @@ -5,8 +5,4 @@ * 2.0. */ -export interface Url { - domain?: string; - full: string; - original?: string; -} +export type { Url } from '@kbn/apm-types/es_schemas_raw'; diff --git a/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/user.ts b/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/user.ts index dcb5fa03dcd5a..a727d61d53005 100644 --- a/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/user.ts +++ b/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/user.ts @@ -5,6 +5,4 @@ * 2.0. */ -export interface User { - id: string; -} +export type { User } from '@kbn/apm-types/es_schemas_raw'; diff --git a/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/user_agent.ts b/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/user_agent.ts index 884f627353d9b..71eb4bd41e434 100644 --- a/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/user_agent.ts +++ b/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/fields/user_agent.ts @@ -5,16 +5,4 @@ * 2.0. */ -export interface UserAgent { - device?: { - name: string; - }; - name?: string; - original: string; - os?: { - name: string; - version?: string; - full?: string; - }; - version?: string; -} +export type { UserAgent } from '@kbn/apm-types/es_schemas_raw'; diff --git a/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/metric_raw.ts b/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/metric_raw.ts index d7d015fd21da5..7bd8dcbe6869c 100644 --- a/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/metric_raw.ts +++ b/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/metric_raw.ts @@ -5,119 +5,4 @@ * 2.0. */ -import { APMBaseDoc } from './apm_base_doc'; -import { Cloud } from './fields/cloud'; -import { Container } from './fields/container'; -import { Host } from './fields/host'; -import { Kubernetes } from './fields/kubernetes'; -import { Service } from './fields/service'; - -type BaseMetric = APMBaseDoc & { - processor: { - name: 'metric'; - event: 'metric'; - }; - cloud?: Cloud; - container?: Container; - kubernetes?: Kubernetes; - service?: Service; - host?: Host; -}; - -type BaseBreakdownMetric = BaseMetric & { - transaction: { - name: string; - type: string; - }; - span: { - self_time: { - count: number; - sum: { - us: number; - }; - }; - }; -}; - -type TransactionBreakdownMetric = BaseBreakdownMetric & { - transaction: { - duration: { - count: number; - sum: { - us: number; - }; - }; - breakdown: { - count: number; - }; - }; -}; - -type SpanBreakdownMetric = BaseBreakdownMetric & { - span: { - type: string; - subtype?: string; - }; -}; - -type SystemMetric = BaseMetric & { - system: unknown; - service: { - node?: { - name: string; - }; - }; -}; - -type CGroupMetric = SystemMetric; -type JVMMetric = SystemMetric & { - jvm: unknown; -}; - -type TransactionDurationMetric = BaseMetric & { - transaction: { - name: string; - type: string; - result?: string; - duration: { - histogram: { - values: number[]; - counts: number[]; - }; - }; - }; - service: { - name: string; - node?: { - name: string; - }; - environment?: string; - version?: string; - }; -}; - -export type SpanDestinationMetric = BaseMetric & { - span: { - destination: { - service: { - resource: string; - response_time: { - count: number; - sum: { - us: number; - }; - }; - }; - }; - }; -}; - -export type MetricRaw = - | BaseMetric - | TransactionBreakdownMetric - | SpanBreakdownMetric - | TransactionDurationMetric - | SpanDestinationMetric - | SystemMetric - | CGroupMetric - | JVMMetric; +export type { MetricRaw } from '@kbn/apm-types/es_schemas_raw'; diff --git a/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/span_raw.ts b/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/span_raw.ts index 301a4c96dfa35..42427f9d8623e 100644 --- a/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/span_raw.ts +++ b/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/span_raw.ts @@ -5,75 +5,4 @@ * 2.0. */ -import { APMBaseDoc } from './apm_base_doc'; -import { EventOutcome } from './fields/event_outcome'; -import { Http } from './fields/http'; -import { SpanLink } from './fields/span_links'; -import { Stackframe } from './fields/stackframe'; -import { TimestampUs } from './fields/timestamp_us'; -import { Url } from './fields/url'; - -interface Processor { - name: 'transaction'; - event: 'span'; -} - -export interface SpanRaw extends APMBaseDoc { - processor: Processor; - trace: { id: string }; // trace is required - event?: { outcome?: EventOutcome }; - service: { - name: string; - environment?: string; - }; - span: { - destination?: { - service: { - resource: string; - }; - }; - action?: string; - duration: { us: number }; - id: string; - name: string; - stacktrace?: Stackframe[]; - subtype?: string; - sync?: boolean; - type: string; - http?: { - url?: { - original?: string; - }; - response: { - status_code: number; - }; - method?: string; - }; - db?: { - statement?: string; - type?: string; - }; - message?: { - queue?: { name: string }; - age?: { ms: number }; - body?: string; - headers?: Record; - }; - composite?: { - count: number; - sum: { us: number }; - compression_strategy: string; - }; - links?: SpanLink[]; - }; - timestamp: TimestampUs; - transaction?: { - id: string; - }; - child?: { id: string[] }; - code?: { - stacktrace?: string; - }; - http?: Http; - url?: Url; -} +export type { SpanRaw } from '@kbn/apm-types/es_schemas_raw'; diff --git a/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/transaction_raw.ts b/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/transaction_raw.ts index 4046bb9470fb7..adfc536ab06cf 100644 --- a/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/transaction_raw.ts +++ b/x-pack/plugins/observability_solution/apm/typings/es_schemas/raw/transaction_raw.ts @@ -5,74 +5,4 @@ * 2.0. */ -import { APMBaseDoc } from './apm_base_doc'; -import { Cloud } from './fields/cloud'; -import { Container } from './fields/container'; -import { EventOutcome } from './fields/event_outcome'; -import { Host } from './fields/host'; -import { Http } from './fields/http'; -import { Kubernetes } from './fields/kubernetes'; -import { Page } from './fields/page'; -import { Process } from './fields/process'; -import { Service } from './fields/service'; -import { TimestampUs } from './fields/timestamp_us'; -import { Url } from './fields/url'; -import { User } from './fields/user'; -import { UserAgent } from './fields/user_agent'; -import { Faas } from './fields/faas'; -import { SpanLink } from './fields/span_links'; - -interface Processor { - name: 'transaction'; - event: 'transaction'; -} - -export interface TransactionRaw extends APMBaseDoc { - processor: Processor; - timestamp: TimestampUs; - trace: { id: string }; // trace is required - event?: { outcome?: EventOutcome }; - transaction: { - duration: { us: number }; - id: string; - marks?: { - // "agent": not defined by APM Server - only sent by RUM agent - agent?: { - [name: string]: number; - }; - }; - name?: string; - page?: Page; // special property for RUM: shared by error and transaction - result?: string; - sampled: boolean; - span_count?: { - started?: number; - dropped?: number; - }; - type: string; - custom?: Record; - message?: { - queue?: { name: string }; - age?: { ms: number }; - body?: string; - headers?: Record; - }; - }; - - // Shared by errors and transactions - container?: Container; - ecs?: { version?: string }; - host?: Host; - http?: Http; - kubernetes?: Kubernetes; - process?: Process; - service: Service; - url?: Url; - user?: User; - user_agent?: UserAgent; - cloud?: Cloud; - faas?: Faas; - span?: { - links?: SpanLink[]; - }; -} +export type { TransactionRaw } from '@kbn/apm-types/es_schemas_raw'; diff --git a/x-pack/plugins/observability_solution/apm/typings/es_schemas/ui/apm_error.ts b/x-pack/plugins/observability_solution/apm/typings/es_schemas/ui/apm_error.ts index 13521d90a84aa..a4c5c7fe0867c 100644 --- a/x-pack/plugins/observability_solution/apm/typings/es_schemas/ui/apm_error.ts +++ b/x-pack/plugins/observability_solution/apm/typings/es_schemas/ui/apm_error.ts @@ -5,9 +5,4 @@ * 2.0. */ -import { ErrorRaw } from '../raw/error_raw'; -import { Agent } from './fields/agent'; - -export interface APMError extends ErrorRaw { - agent: Agent; -} +export type { APMError } from '@kbn/apm-types/es_schemas_ui'; diff --git a/x-pack/plugins/observability_solution/apm/typings/es_schemas/ui/event.ts b/x-pack/plugins/observability_solution/apm/typings/es_schemas/ui/event.ts index 8d9fccea1c8bf..de726d110914c 100644 --- a/x-pack/plugins/observability_solution/apm/typings/es_schemas/ui/event.ts +++ b/x-pack/plugins/observability_solution/apm/typings/es_schemas/ui/event.ts @@ -5,9 +5,4 @@ * 2.0. */ -import { EventRaw } from '../raw/event_raw'; -import { Agent } from './fields/agent'; - -export interface Event extends EventRaw { - agent: Agent; -} +export type { Event } from '@kbn/apm-types/es_schemas_ui'; diff --git a/x-pack/plugins/observability_solution/apm/typings/es_schemas/ui/fields/agent.ts b/x-pack/plugins/observability_solution/apm/typings/es_schemas/ui/fields/agent.ts index 67f4a6b2ba10b..b53da484ec747 100644 --- a/x-pack/plugins/observability_solution/apm/typings/es_schemas/ui/fields/agent.ts +++ b/x-pack/plugins/observability_solution/apm/typings/es_schemas/ui/fields/agent.ts @@ -5,12 +5,9 @@ * 2.0. */ -import type { AgentName } from '@kbn/elastic-agent-utils'; - -export type { ElasticAgentName, OpenTelemetryAgentName, AgentName } from '@kbn/elastic-agent-utils'; - -export interface Agent { - ephemeral_id?: string; - name: AgentName; - version: string; -} +export type { + Agent, + ElasticAgentName, + OpenTelemetryAgentName, + AgentName, +} from '@kbn/apm-types/es_schemas_ui'; diff --git a/x-pack/plugins/observability_solution/apm/typings/es_schemas/ui/metric.ts b/x-pack/plugins/observability_solution/apm/typings/es_schemas/ui/metric.ts index b06a686c23ef7..d2c9833391564 100644 --- a/x-pack/plugins/observability_solution/apm/typings/es_schemas/ui/metric.ts +++ b/x-pack/plugins/observability_solution/apm/typings/es_schemas/ui/metric.ts @@ -5,6 +5,4 @@ * 2.0. */ -import { MetricRaw } from '../raw/metric_raw'; - -export type Metric = MetricRaw; +export type { Metric } from '@kbn/apm-types/es_schemas_ui'; diff --git a/x-pack/plugins/observability_solution/apm/typings/es_schemas/ui/span.ts b/x-pack/plugins/observability_solution/apm/typings/es_schemas/ui/span.ts index cfee36de51429..92e2fd44eabd8 100644 --- a/x-pack/plugins/observability_solution/apm/typings/es_schemas/ui/span.ts +++ b/x-pack/plugins/observability_solution/apm/typings/es_schemas/ui/span.ts @@ -5,9 +5,4 @@ * 2.0. */ -import { SpanRaw } from '../raw/span_raw'; -import { Agent } from './fields/agent'; - -export interface Span extends SpanRaw { - agent: Agent; -} +export type { Span } from '@kbn/apm-types/es_schemas_ui'; diff --git a/x-pack/plugins/observability_solution/apm/typings/es_schemas/ui/transaction.ts b/x-pack/plugins/observability_solution/apm/typings/es_schemas/ui/transaction.ts index 2dfbc860ec05a..db8012401f398 100644 --- a/x-pack/plugins/observability_solution/apm/typings/es_schemas/ui/transaction.ts +++ b/x-pack/plugins/observability_solution/apm/typings/es_schemas/ui/transaction.ts @@ -5,18 +5,4 @@ * 2.0. */ -import { TransactionRaw } from '../raw/transaction_raw'; -import { Agent } from './fields/agent'; - -// Make `transaction.name` required instead of optional. -// `transaction.name` can be missing in Elasticsearch but the UI will only aggregate on transactions with a name, -// and thus it doesn't make sense to treat it as optional -type InnerTransaction = TransactionRaw['transaction']; -interface InnerTransactionWithName extends InnerTransaction { - name: string; -} - -export interface Transaction extends TransactionRaw { - agent: Agent; - transaction: InnerTransactionWithName; -} +export type { Transaction } from '@kbn/apm-types/es_schemas_ui'; diff --git a/yarn.lock b/yarn.lock index 9623b2b9378f3..3d2a400a9f70f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3452,6 +3452,10 @@ version "0.0.0" uid "" +"@kbn/apm-types@link:packages/kbn-apm-types": + version "0.0.0" + uid "" + "@kbn/apm-utils@link:packages/kbn-apm-utils": version "0.0.0" uid ""