diff --git a/.buildkite/scripts/steps/test/jest_parallel.sh b/.buildkite/scripts/steps/test/jest_parallel.sh index 0530fe0f8416..27eb9e921636 100755 --- a/.buildkite/scripts/steps/test/jest_parallel.sh +++ b/.buildkite/scripts/steps/test/jest_parallel.sh @@ -10,10 +10,17 @@ JOB_COUNT=$BUILDKITE_PARALLEL_JOB_COUNT i=0 exitCode=0 +# run unit tests in parallel +if [[ "$1" == 'jest.config.js' ]]; then + parallelism="-w2" +else + parallelism="--runInBand" +fi + while read -r config; do if [ "$((i % JOB_COUNT))" -eq "$JOB" ]; then echo "--- $ node scripts/jest --config $config" - node --max-old-space-size=14336 ./scripts/jest --config="$config" --runInBand --coverage=false --passWithNoTests + node --max-old-space-size=14336 ./scripts/jest --config="$config" "$parallelism" --coverage=false --passWithNoTests lastCode=$? if [ $lastCode -ne 0 ]; then diff --git a/.buildkite/yarn.lock b/.buildkite/yarn.lock index c2d6928d30c5..c5a4e404ba97 100644 --- a/.buildkite/yarn.lock +++ b/.buildkite/yarn.lock @@ -3,93 +3,93 @@ "@octokit/auth-token@^2.4.4": - "integrity" "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==" - "resolved" "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz" - "version" "2.5.0" + version "2.5.0" + resolved "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz" + integrity sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g== dependencies: "@octokit/types" "^6.0.3" -"@octokit/core@^3.5.1", "@octokit/core@>=2", "@octokit/core@>=3": - "integrity" "sha512-omncwpLVxMP+GLpLPgeGJBF6IWJFjXDS5flY5VbppePYX9XehevbDykRH9PdCdvqt9TS5AOTiDide7h0qrkHjw==" - "resolved" "https://registry.npmjs.org/@octokit/core/-/core-3.5.1.tgz" - "version" "3.5.1" +"@octokit/core@^3.5.1": + version "3.5.1" + resolved "https://registry.npmjs.org/@octokit/core/-/core-3.5.1.tgz" + integrity sha512-omncwpLVxMP+GLpLPgeGJBF6IWJFjXDS5flY5VbppePYX9XehevbDykRH9PdCdvqt9TS5AOTiDide7h0qrkHjw== dependencies: "@octokit/auth-token" "^2.4.4" "@octokit/graphql" "^4.5.8" "@octokit/request" "^5.6.0" "@octokit/request-error" "^2.0.5" "@octokit/types" "^6.0.3" - "before-after-hook" "^2.2.0" - "universal-user-agent" "^6.0.0" + before-after-hook "^2.2.0" + universal-user-agent "^6.0.0" "@octokit/endpoint@^6.0.1": - "integrity" "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==" - "resolved" "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz" - "version" "6.0.12" + version "6.0.12" + resolved "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz" + integrity sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA== dependencies: "@octokit/types" "^6.0.3" - "is-plain-object" "^5.0.0" - "universal-user-agent" "^6.0.0" + is-plain-object "^5.0.0" + universal-user-agent "^6.0.0" "@octokit/graphql@^4.5.8": - "integrity" "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==" - "resolved" "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz" - "version" "4.8.0" + version "4.8.0" + resolved "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz" + integrity sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg== dependencies: "@octokit/request" "^5.6.0" "@octokit/types" "^6.0.3" - "universal-user-agent" "^6.0.0" + universal-user-agent "^6.0.0" "@octokit/openapi-types@^11.2.0": - "integrity" "sha512-PBsVO+15KSlGmiI8QAzaqvsNlZlrDlyAJYcrXBCvVUxCp7VnXjkwPoFHgjEJXx3WF9BAwkA6nfCUA7i9sODzKA==" - "resolved" "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-11.2.0.tgz" - "version" "11.2.0" + version "11.2.0" + resolved "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-11.2.0.tgz" + integrity sha512-PBsVO+15KSlGmiI8QAzaqvsNlZlrDlyAJYcrXBCvVUxCp7VnXjkwPoFHgjEJXx3WF9BAwkA6nfCUA7i9sODzKA== "@octokit/plugin-paginate-rest@^2.16.8": - "integrity" "sha512-tzMbrbnam2Mt4AhuyCHvpRkS0oZ5MvwwcQPYGtMv4tUa5kkzG58SVB0fcsLulOZQeRnOgdkZWkRUiyBlh0Bkyw==" - "resolved" "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.17.0.tgz" - "version" "2.17.0" + version "2.17.0" + resolved "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.17.0.tgz" + integrity sha512-tzMbrbnam2Mt4AhuyCHvpRkS0oZ5MvwwcQPYGtMv4tUa5kkzG58SVB0fcsLulOZQeRnOgdkZWkRUiyBlh0Bkyw== dependencies: "@octokit/types" "^6.34.0" "@octokit/plugin-request-log@^1.0.4": - "integrity" "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==" - "resolved" "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz" - "version" "1.0.4" + version "1.0.4" + resolved "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz" + integrity sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA== "@octokit/plugin-rest-endpoint-methods@^5.12.0": - "integrity" "sha512-uJjMTkN1KaOIgNtUPMtIXDOjx6dGYysdIFhgA52x4xSadQCz3b/zJexvITDVpANnfKPW/+E0xkOvLntqMYpviA==" - "resolved" "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.13.0.tgz" - "version" "5.13.0" + version "5.13.0" + resolved "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.13.0.tgz" + integrity sha512-uJjMTkN1KaOIgNtUPMtIXDOjx6dGYysdIFhgA52x4xSadQCz3b/zJexvITDVpANnfKPW/+E0xkOvLntqMYpviA== dependencies: "@octokit/types" "^6.34.0" - "deprecation" "^2.3.1" + deprecation "^2.3.1" "@octokit/request-error@^2.0.5", "@octokit/request-error@^2.1.0": - "integrity" "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==" - "resolved" "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz" - "version" "2.1.0" + version "2.1.0" + resolved "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz" + integrity sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg== dependencies: "@octokit/types" "^6.0.3" - "deprecation" "^2.0.0" - "once" "^1.4.0" + deprecation "^2.0.0" + once "^1.4.0" "@octokit/request@^5.6.0": - "integrity" "sha512-je66CvSEVf0jCpRISxkUcCa0UkxmFs6eGDRSbfJtAVwbLH5ceqF+YEyC8lj8ystKyZTy8adWr0qmkY52EfOeLA==" - "resolved" "https://registry.npmjs.org/@octokit/request/-/request-5.6.2.tgz" - "version" "5.6.2" + version "5.6.2" + resolved "https://registry.npmjs.org/@octokit/request/-/request-5.6.2.tgz" + integrity sha512-je66CvSEVf0jCpRISxkUcCa0UkxmFs6eGDRSbfJtAVwbLH5ceqF+YEyC8lj8ystKyZTy8adWr0qmkY52EfOeLA== dependencies: "@octokit/endpoint" "^6.0.1" "@octokit/request-error" "^2.1.0" "@octokit/types" "^6.16.1" - "is-plain-object" "^5.0.0" - "node-fetch" "^2.6.1" - "universal-user-agent" "^6.0.0" + is-plain-object "^5.0.0" + node-fetch "^2.6.1" + universal-user-agent "^6.0.0" "@octokit/rest@^18.10.0": - "integrity" "sha512-gDPiOHlyGavxr72y0guQEhLsemgVjwRePayJ+FcKc2SJqKUbxbkvf5kAZEWA/MKvsfYlQAMVzNJE3ezQcxMJ2Q==" - "resolved" "https://registry.npmjs.org/@octokit/rest/-/rest-18.12.0.tgz" - "version" "18.12.0" + version "18.12.0" + resolved "https://registry.npmjs.org/@octokit/rest/-/rest-18.12.0.tgz" + integrity sha512-gDPiOHlyGavxr72y0guQEhLsemgVjwRePayJ+FcKc2SJqKUbxbkvf5kAZEWA/MKvsfYlQAMVzNJE3ezQcxMJ2Q== dependencies: "@octokit/core" "^3.5.1" "@octokit/plugin-paginate-rest" "^2.16.8" @@ -97,84 +97,84 @@ "@octokit/plugin-rest-endpoint-methods" "^5.12.0" "@octokit/types@^6.0.3", "@octokit/types@^6.16.1", "@octokit/types@^6.34.0": - "integrity" "sha512-s1zLBjWhdEI2zwaoSgyOFoKSl109CUcVBCc7biPJ3aAf6LGLU6szDvi31JPU7bxfla2lqfhjbbg/5DdFNxOwHw==" - "resolved" "https://registry.npmjs.org/@octokit/types/-/types-6.34.0.tgz" - "version" "6.34.0" + version "6.34.0" + resolved "https://registry.npmjs.org/@octokit/types/-/types-6.34.0.tgz" + integrity sha512-s1zLBjWhdEI2zwaoSgyOFoKSl109CUcVBCc7biPJ3aAf6LGLU6szDvi31JPU7bxfla2lqfhjbbg/5DdFNxOwHw== dependencies: "@octokit/openapi-types" "^11.2.0" -"axios@^0.21.4": - "integrity" "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==" - "resolved" "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz" - "version" "0.21.4" +axios@^0.21.4: + version "0.21.4" + resolved "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz" + integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg== dependencies: - "follow-redirects" "^1.14.0" - -"before-after-hook@^2.2.0": - "integrity" "sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ==" - "resolved" "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.2.tgz" - "version" "2.2.2" - -"deprecation@^2.0.0", "deprecation@^2.3.1": - "integrity" "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" - "resolved" "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz" - "version" "2.3.1" - -"follow-redirects@^1.14.0": - "integrity" "sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA==" - "resolved" "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz" - "version" "1.14.5" - -"is-plain-object@^5.0.0": - "integrity" "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==" - "resolved" "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz" - "version" "5.0.0" - -"kibana-buildkite-library@github:elastic/kibana-buildkite-library": - "resolved" "git+ssh://git@github.com/elastic/kibana-buildkite-library.git#ccf5b824c4294d1fdf3569d32218d3bdb0958121" - "version" "1.0.0" + follow-redirects "^1.14.0" + +before-after-hook@^2.2.0: + version "2.2.2" + resolved "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.2.tgz" + integrity sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ== + +deprecation@^2.0.0, deprecation@^2.3.1: + version "2.3.1" + resolved "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz" + integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ== + +follow-redirects@^1.14.0: + version "1.14.5" + resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz" + integrity sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA== + +is-plain-object@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz" + integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== + +kibana-buildkite-library@elastic/kibana-buildkite-library: + version "1.0.0" + resolved "https://codeload.github.com/elastic/kibana-buildkite-library/tar.gz/bd0bec4c7af5f64a12c781d03cedb9fb2386bfbd" dependencies: "@octokit/rest" "^18.10.0" - "axios" "^0.21.4" + axios "^0.21.4" -"node-fetch@^2.6.1": - "integrity" "sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==" - "resolved" "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz" - "version" "2.6.6" +node-fetch@^2.6.1: + version "2.6.6" + resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz" + integrity sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA== dependencies: - "whatwg-url" "^5.0.0" + whatwg-url "^5.0.0" -"once@^1.4.0": - "integrity" "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=" - "resolved" "https://registry.npmjs.org/once/-/once-1.4.0.tgz" - "version" "1.4.0" +once@^1.4.0: + version "1.4.0" + resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= dependencies: - "wrappy" "1" - -"tr46@~0.0.3": - "integrity" "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" - "resolved" "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz" - "version" "0.0.3" - -"universal-user-agent@^6.0.0": - "integrity" "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==" - "resolved" "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz" - "version" "6.0.0" - -"webidl-conversions@^3.0.0": - "integrity" "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" - "resolved" "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz" - "version" "3.0.1" - -"whatwg-url@^5.0.0": - "integrity" "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=" - "resolved" "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz" - "version" "5.0.0" + wrappy "1" + +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz" + integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= + +universal-user-agent@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz" + integrity sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w== + +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz" + integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= + +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz" + integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0= dependencies: - "tr46" "~0.0.3" - "webidl-conversions" "^3.0.0" + tr46 "~0.0.3" + webidl-conversions "^3.0.0" -"wrappy@1": - "integrity" "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - "resolved" "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" - "version" "1.0.2" +wrappy@1: + version "1.0.2" + resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 89cf1fdf3855..869160bfa0fd 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -133,7 +133,7 @@ #CC# /x-pack/plugins/observability/ @elastic/apm-ui # Uptime -/x-pack/plugins/uptime @elastic/uptime +/x-pack/plugins/synthetics @elastic/uptime /x-pack/plugins/ux @elastic/uptime /x-pack/test/functional_with_es_ssl/apps/uptime @elastic/uptime /x-pack/test/functional/apps/uptime @elastic/uptime diff --git a/config/kibana.yml b/config/kibana.yml index 50ddad9a4b32..4233bf2882a2 100644 --- a/config/kibana.yml +++ b/config/kibana.yml @@ -159,8 +159,8 @@ # =================== Search Autocomplete =================== # Time in milliseconds to wait for autocomplete suggestions from Elasticsearch. # This value must be a whole number greater than zero. Defaults to 1000ms -#data.autocomplete.valueSuggestions.timeout: 1000 +#unifiedSearch.autocomplete.valueSuggestions.timeout: 1000 # Maximum number of documents loaded by each shard to generate autocomplete suggestions. # This value must be a whole number greater than zero. Defaults to 100_000 -#data.autocomplete.valueSuggestions.terminateAfter: 100000 +#unifiedSearch.autocomplete.valueSuggestions.terminateAfter: 100000 diff --git a/docs/development/core/server/kibana-plugin-core-server.corerequesthandlercontext.deprecations.md b/docs/development/core/server/kibana-plugin-core-server.corerequesthandlercontext.deprecations.md new file mode 100644 index 000000000000..51d93371e518 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.corerequesthandlercontext.deprecations.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [CoreRequestHandlerContext](./kibana-plugin-core-server.corerequesthandlercontext.md) > [deprecations](./kibana-plugin-core-server.corerequesthandlercontext.deprecations.md) + +## CoreRequestHandlerContext.deprecations property + +Signature: + +```typescript +deprecations: { + client: DeprecationsClient; + }; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.corerequesthandlercontext.elasticsearch.md b/docs/development/core/server/kibana-plugin-core-server.corerequesthandlercontext.elasticsearch.md new file mode 100644 index 000000000000..22ff84ce1cb5 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.corerequesthandlercontext.elasticsearch.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [CoreRequestHandlerContext](./kibana-plugin-core-server.corerequesthandlercontext.md) > [elasticsearch](./kibana-plugin-core-server.corerequesthandlercontext.elasticsearch.md) + +## CoreRequestHandlerContext.elasticsearch property + +Signature: + +```typescript +elasticsearch: { + client: IScopedClusterClient; + }; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.corerequesthandlercontext.md b/docs/development/core/server/kibana-plugin-core-server.corerequesthandlercontext.md new file mode 100644 index 000000000000..47297bcddc90 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.corerequesthandlercontext.md @@ -0,0 +1,25 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [CoreRequestHandlerContext](./kibana-plugin-core-server.corerequesthandlercontext.md) + +## CoreRequestHandlerContext interface + +The `core` context provided to route handler. + +Provides the following clients and services: - [savedObjects.client](./kibana-plugin-core-server.savedobjectsclient.md) - Saved Objects client which uses the credentials of the incoming request - [savedObjects.typeRegistry](./kibana-plugin-core-server.isavedobjecttyperegistry.md) - Type registry containing all the registered types. - [elasticsearch.client](./kibana-plugin-core-server.iscopedclusterclient.md) - Elasticsearch data client which uses the credentials of the incoming request - [uiSettings.client](./kibana-plugin-core-server.iuisettingsclient.md) - uiSettings client which uses the credentials of the incoming request + +Signature: + +```typescript +export interface CoreRequestHandlerContext +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [deprecations](./kibana-plugin-core-server.corerequesthandlercontext.deprecations.md) | { client: DeprecationsClient; } | | +| [elasticsearch](./kibana-plugin-core-server.corerequesthandlercontext.elasticsearch.md) | { client: IScopedClusterClient; } | | +| [savedObjects](./kibana-plugin-core-server.corerequesthandlercontext.savedobjects.md) | { client: SavedObjectsClientContract; typeRegistry: ISavedObjectTypeRegistry; getClient: (options?: SavedObjectsClientProviderOptions) => SavedObjectsClientContract; getExporter: (client: SavedObjectsClientContract) => ISavedObjectsExporter; getImporter: (client: SavedObjectsClientContract) => ISavedObjectsImporter; } | | +| [uiSettings](./kibana-plugin-core-server.corerequesthandlercontext.uisettings.md) | { client: IUiSettingsClient; } | | + diff --git a/docs/development/core/server/kibana-plugin-core-server.corerequesthandlercontext.savedobjects.md b/docs/development/core/server/kibana-plugin-core-server.corerequesthandlercontext.savedobjects.md new file mode 100644 index 000000000000..6d1aa0a8bb5f --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.corerequesthandlercontext.savedobjects.md @@ -0,0 +1,17 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [CoreRequestHandlerContext](./kibana-plugin-core-server.corerequesthandlercontext.md) > [savedObjects](./kibana-plugin-core-server.corerequesthandlercontext.savedobjects.md) + +## CoreRequestHandlerContext.savedObjects property + +Signature: + +```typescript +savedObjects: { + client: SavedObjectsClientContract; + typeRegistry: ISavedObjectTypeRegistry; + getClient: (options?: SavedObjectsClientProviderOptions) => SavedObjectsClientContract; + getExporter: (client: SavedObjectsClientContract) => ISavedObjectsExporter; + getImporter: (client: SavedObjectsClientContract) => ISavedObjectsImporter; + }; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.corerequesthandlercontext.uisettings.md b/docs/development/core/server/kibana-plugin-core-server.corerequesthandlercontext.uisettings.md new file mode 100644 index 000000000000..f76dcc8965a0 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.corerequesthandlercontext.uisettings.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [CoreRequestHandlerContext](./kibana-plugin-core-server.corerequesthandlercontext.md) > [uiSettings](./kibana-plugin-core-server.corerequesthandlercontext.uisettings.md) + +## CoreRequestHandlerContext.uiSettings property + +Signature: + +```typescript +uiSettings: { + client: IUiSettingsClient; + }; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.customrequesthandlercontext.md b/docs/development/core/server/kibana-plugin-core-server.customrequesthandlercontext.md new file mode 100644 index 000000000000..afaf8c278565 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.customrequesthandlercontext.md @@ -0,0 +1,14 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [CustomRequestHandlerContext](./kibana-plugin-core-server.customrequesthandlercontext.md) + +## CustomRequestHandlerContext type + + +Signature: + +```typescript +export declare type CustomRequestHandlerContext = RequestHandlerContext & { + [Key in keyof T]: T[Key] extends Promise ? T[Key] : Promise; +}; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.httpservicesetup.md b/docs/development/core/server/kibana-plugin-core-server.httpservicesetup.md index f3be1a9130b9..bb03887e547e 100644 --- a/docs/development/core/server/kibana-plugin-core-server.httpservicesetup.md +++ b/docs/development/core/server/kibana-plugin-core-server.httpservicesetup.md @@ -87,5 +87,5 @@ async (context, request, response) => { | [registerOnPreAuth](./kibana-plugin-core-server.httpservicesetup.registeronpreauth.md) | (handler: OnPreAuthHandler) => void | To define custom logic to perform for incoming requests before the Auth interceptor performs a check that user has access to requested resources. | | [registerOnPreResponse](./kibana-plugin-core-server.httpservicesetup.registeronpreresponse.md) | (handler: OnPreResponseHandler) => void | To define custom logic to perform for the server response. | | [registerOnPreRouting](./kibana-plugin-core-server.httpservicesetup.registeronprerouting.md) | (handler: OnPreRoutingHandler) => void | To define custom logic to perform for incoming requests before server performs a route lookup. | -| [registerRouteHandlerContext](./kibana-plugin-core-server.httpservicesetup.registerroutehandlercontext.md) | <Context extends RequestHandlerContext, ContextName extends keyof Context>(contextName: ContextName, provider: RequestHandlerContextProvider<Context, ContextName>) => RequestHandlerContextContainer | Register a context provider for a route handler. | +| [registerRouteHandlerContext](./kibana-plugin-core-server.httpservicesetup.registerroutehandlercontext.md) | <Context extends RequestHandlerContext, ContextName extends keyof Omit<Context, 'resolve'>>(contextName: ContextName, provider: RequestHandlerContextProvider<Context, ContextName>) => RequestHandlerContextContainer | Register a context provider for a route handler. | diff --git a/docs/development/core/server/kibana-plugin-core-server.httpservicesetup.registerroutehandlercontext.md b/docs/development/core/server/kibana-plugin-core-server.httpservicesetup.registerroutehandlercontext.md index c793080305d0..23e009864dcd 100644 --- a/docs/development/core/server/kibana-plugin-core-server.httpservicesetup.registerroutehandlercontext.md +++ b/docs/development/core/server/kibana-plugin-core-server.httpservicesetup.registerroutehandlercontext.md @@ -9,7 +9,7 @@ Register a context provider for a route handler. Signature: ```typescript -registerRouteHandlerContext: (contextName: ContextName, provider: RequestHandlerContextProvider) => RequestHandlerContextContainer; +registerRouteHandlerContext: >(contextName: ContextName, provider: RequestHandlerContextProvider) => RequestHandlerContextContainer; ``` ## Example diff --git a/docs/development/core/server/kibana-plugin-core-server.icontextprovider.md b/docs/development/core/server/kibana-plugin-core-server.icontextprovider.md index ddd8a0e92f46..eb2ec3cbf90b 100644 --- a/docs/development/core/server/kibana-plugin-core-server.icontextprovider.md +++ b/docs/development/core/server/kibana-plugin-core-server.icontextprovider.md @@ -9,7 +9,7 @@ A function that returns a context value for a specific key of given context type Signature: ```typescript -export declare type IContextProvider = (context: Omit, ...rest: HandlerParameters) => Promise | Context[ContextName]; +export declare type IContextProvider = (context: Omit, ...rest: HandlerParameters) => MaybePromise>; ``` ## Remarks diff --git a/docs/development/core/server/kibana-plugin-core-server.md b/docs/development/core/server/kibana-plugin-core-server.md index d142579e1ced..e33beb19e01a 100644 --- a/docs/development/core/server/kibana-plugin-core-server.md +++ b/docs/development/core/server/kibana-plugin-core-server.md @@ -60,6 +60,7 @@ The plugin integrates with the core system via lifecycle events: `setup` | [ConfigDeprecationDetails](./kibana-plugin-core-server.configdeprecationdetails.md) | | | [ContextSetup](./kibana-plugin-core-server.contextsetup.md) | An object that handles registration of context providers and configuring handlers with context. | | [CorePreboot](./kibana-plugin-core-server.corepreboot.md) | Context passed to the setup method of preboot plugins. | +| [CoreRequestHandlerContext](./kibana-plugin-core-server.corerequesthandlercontext.md) | The core context provided to route handler.Provides the following clients and services: - [savedObjects.client](./kibana-plugin-core-server.savedobjectsclient.md) - Saved Objects client which uses the credentials of the incoming request - [savedObjects.typeRegistry](./kibana-plugin-core-server.isavedobjecttyperegistry.md) - Type registry containing all the registered types. - [elasticsearch.client](./kibana-plugin-core-server.iscopedclusterclient.md) - Elasticsearch data client which uses the credentials of the incoming request - [uiSettings.client](./kibana-plugin-core-server.iuisettingsclient.md) - uiSettings client which uses the credentials of the incoming request | | [CoreSetup](./kibana-plugin-core-server.coresetup.md) | Context passed to the setup method of standard plugins. | | [CoreStart](./kibana-plugin-core-server.corestart.md) | Context passed to the plugins start method. | | [CoreStatus](./kibana-plugin-core-server.corestatus.md) | Status of core services. | @@ -133,7 +134,8 @@ The plugin integrates with the core system via lifecycle events: `setup` | [PrebootPlugin](./kibana-plugin-core-server.prebootplugin.md) | The interface that should be returned by a PluginInitializer for a preboot plugin. | | [PrebootServicePreboot](./kibana-plugin-core-server.prebootservicepreboot.md) | Kibana Preboot Service allows to control the boot flow of Kibana. Preboot plugins can use it to hold the boot until certain condition is met. | | [RegisterDeprecationsConfig](./kibana-plugin-core-server.registerdeprecationsconfig.md) | | -| [RequestHandlerContext](./kibana-plugin-core-server.requesthandlercontext.md) | Plugin specific context passed to a route handler.Provides the following clients and services: - [savedObjects.client](./kibana-plugin-core-server.savedobjectsclient.md) - Saved Objects client which uses the credentials of the incoming request - [savedObjects.typeRegistry](./kibana-plugin-core-server.isavedobjecttyperegistry.md) - Type registry containing all the registered types. - [elasticsearch.client](./kibana-plugin-core-server.iscopedclusterclient.md) - Elasticsearch data client which uses the credentials of the incoming request - [uiSettings.client](./kibana-plugin-core-server.iuisettingsclient.md) - uiSettings client which uses the credentials of the incoming request | +| [RequestHandlerContext](./kibana-plugin-core-server.requesthandlercontext.md) | Base context passed to a route handler. | +| [RequestHandlerContextBase](./kibana-plugin-core-server.requesthandlercontextbase.md) | \* | | [ResolveCapabilitiesOptions](./kibana-plugin-core-server.resolvecapabilitiesoptions.md) | Defines a set of additional options for the resolveCapabilities method of [CapabilitiesStart](./kibana-plugin-core-server.capabilitiesstart.md). | | [RouteConfig](./kibana-plugin-core-server.routeconfig.md) | Route specific configuration. | | [RouteConfigOptions](./kibana-plugin-core-server.routeconfigoptions.md) | Additional route options. | @@ -262,6 +264,7 @@ The plugin integrates with the core system via lifecycle events: `setup` | [AuthResult](./kibana-plugin-core-server.authresult.md) | | | [CapabilitiesProvider](./kibana-plugin-core-server.capabilitiesprovider.md) | See [CapabilitiesSetup](./kibana-plugin-core-server.capabilitiessetup.md) | | [CapabilitiesSwitcher](./kibana-plugin-core-server.capabilitiesswitcher.md) | See [CapabilitiesSetup](./kibana-plugin-core-server.capabilitiessetup.md) | +| [CustomRequestHandlerContext](./kibana-plugin-core-server.customrequesthandlercontext.md) | | | [DeprecationsDetails](./kibana-plugin-core-server.deprecationsdetails.md) | | | [DestructiveRouteMethod](./kibana-plugin-core-server.destructiveroutemethod.md) | Set of HTTP methods changing the state of the server. | | [DocLinksServiceStart](./kibana-plugin-core-server.doclinksservicestart.md) | | diff --git a/docs/development/core/server/kibana-plugin-core-server.requesthandlercontext.core.md b/docs/development/core/server/kibana-plugin-core-server.requesthandlercontext.core.md index dcf6975c5fa7..8d0b715fdef7 100644 --- a/docs/development/core/server/kibana-plugin-core-server.requesthandlercontext.core.md +++ b/docs/development/core/server/kibana-plugin-core-server.requesthandlercontext.core.md @@ -7,22 +7,5 @@ Signature: ```typescript -core: { - savedObjects: { - client: SavedObjectsClientContract; - typeRegistry: ISavedObjectTypeRegistry; - getClient: (options?: SavedObjectsClientProviderOptions) => SavedObjectsClientContract; - getExporter: (client: SavedObjectsClientContract) => ISavedObjectsExporter; - getImporter: (client: SavedObjectsClientContract) => ISavedObjectsImporter; - }; - elasticsearch: { - client: IScopedClusterClient; - }; - uiSettings: { - client: IUiSettingsClient; - }; - deprecations: { - client: DeprecationsClient; - }; - }; +core: Promise; ``` diff --git a/docs/development/core/server/kibana-plugin-core-server.requesthandlercontext.md b/docs/development/core/server/kibana-plugin-core-server.requesthandlercontext.md index 0d705c9daa33..214f8a6f6ba5 100644 --- a/docs/development/core/server/kibana-plugin-core-server.requesthandlercontext.md +++ b/docs/development/core/server/kibana-plugin-core-server.requesthandlercontext.md @@ -4,19 +4,18 @@ ## RequestHandlerContext interface -Plugin specific context passed to a route handler. - -Provides the following clients and services: - [savedObjects.client](./kibana-plugin-core-server.savedobjectsclient.md) - Saved Objects client which uses the credentials of the incoming request - [savedObjects.typeRegistry](./kibana-plugin-core-server.isavedobjecttyperegistry.md) - Type registry containing all the registered types. - [elasticsearch.client](./kibana-plugin-core-server.iscopedclusterclient.md) - Elasticsearch data client which uses the credentials of the incoming request - [uiSettings.client](./kibana-plugin-core-server.iuisettingsclient.md) - uiSettings client which uses the credentials of the incoming request +Base context passed to a route handler. Signature: ```typescript -export interface RequestHandlerContext +export interface RequestHandlerContext extends RequestHandlerContextBase ``` +Extends: RequestHandlerContextBase ## Properties | Property | Type | Description | | --- | --- | --- | -| [core](./kibana-plugin-core-server.requesthandlercontext.core.md) | { savedObjects: { client: SavedObjectsClientContract; typeRegistry: ISavedObjectTypeRegistry; getClient: (options?: SavedObjectsClientProviderOptions) => SavedObjectsClientContract; getExporter: (client: SavedObjectsClientContract) => ISavedObjectsExporter; getImporter: (client: SavedObjectsClientContract) => ISavedObjectsImporter; }; elasticsearch: { client: IScopedClusterClient; }; uiSettings: { client: IUiSettingsClient; }; deprecations: { client: DeprecationsClient; }; } | | +| [core](./kibana-plugin-core-server.requesthandlercontext.core.md) | Promise<CoreRequestHandlerContext> | | diff --git a/docs/development/core/server/kibana-plugin-core-server.requesthandlercontextbase.md b/docs/development/core/server/kibana-plugin-core-server.requesthandlercontextbase.md new file mode 100644 index 000000000000..33a123eefae6 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.requesthandlercontextbase.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [RequestHandlerContextBase](./kibana-plugin-core-server.requesthandlercontextbase.md) + +## RequestHandlerContextBase interface + +\* + +Signature: + +```typescript +export interface RequestHandlerContextBase +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [resolve](./kibana-plugin-core-server.requesthandlercontextbase.resolve.md) | <T extends keyof Omit<this, 'resolve'>>(parts: T\[\]) => Promise<AwaitedProperties<Pick<this, T>>> | Await all the specified context parts and return them. | + diff --git a/docs/development/core/server/kibana-plugin-core-server.requesthandlercontextbase.resolve.md b/docs/development/core/server/kibana-plugin-core-server.requesthandlercontextbase.resolve.md new file mode 100644 index 000000000000..74192c0e1aee --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.requesthandlercontextbase.resolve.md @@ -0,0 +1,23 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [RequestHandlerContextBase](./kibana-plugin-core-server.requesthandlercontextbase.md) > [resolve](./kibana-plugin-core-server.requesthandlercontextbase.resolve.md) + +## RequestHandlerContextBase.resolve property + +Await all the specified context parts and return them. + +Signature: + +```typescript +resolve: >(parts: T[]) => Promise>>; +``` + +## Example + + +```ts +const resolved = await context.resolve(['core', 'pluginA']); +const esClient = resolved.core.elasticsearch.client; +const pluginAService = resolved.pluginA.someService; +``` + diff --git a/docs/setup/settings.asciidoc b/docs/setup/settings.asciidoc index f5dca28a7da1..2d52abc1f013 100644 --- a/docs/setup/settings.asciidoc +++ b/docs/setup/settings.asciidoc @@ -289,11 +289,11 @@ is an alternative to `elasticsearch.username` and `elasticsearch.password`. | `interpreter.enableInVisualize` | Enables use of interpreter in Visualize. *Default: `true`* -| `data.autocomplete.valueSuggestions.timeout:` {ess-icon} +| `unifiedSearch.autocomplete.valueSuggestions.timeout:` {ess-icon} | Time in milliseconds to wait for autocomplete suggestions from {es}. This value must be a whole number greater than zero. *Default: `"1000"`* -| `data.autocomplete.valueSuggestions.terminateAfter:` {ess-icon} +| `unifiedSearch.autocomplete.valueSuggestions.terminateAfter:` {ess-icon} | Maximum number of documents loaded by each shard to generate autocomplete suggestions. This value must be a whole number greater than zero. *Default: `"100000"`* diff --git a/examples/screenshot_mode_example/server/routes.ts b/examples/screenshot_mode_example/server/routes.ts index adf4c2e2b6fc..44515abc48c7 100644 --- a/examples/screenshot_mode_example/server/routes.ts +++ b/examples/screenshot_mode_example/server/routes.ts @@ -14,7 +14,7 @@ export const registerRoutes = ({ router, log, screenshotMode }: RouteDependencie { path: `${BASE_API_ROUTE}/check_is_screenshot`, validate: false }, async (ctx, req, res) => { log.info(`Reading screenshot mode from a request: ${screenshotMode.isScreenshotMode(req)}`); - log.info(`Reading is screenshot mode from ctx: ${ctx.screenshotMode.isScreenshot}`); + log.info(`Reading is screenshot mode from ctx: ${(await ctx.screenshotMode).isScreenshot}`); return res.ok(); } ); diff --git a/examples/search_examples/server/routes/server_search_route.ts b/examples/search_examples/server/routes/server_search_route.ts index d7464561a0ab..632952b6c461 100644 --- a/examples/search_examples/server/routes/server_search_route.ts +++ b/examples/search_examples/server/routes/server_search_route.ts @@ -33,8 +33,9 @@ export function registerServerSearchRoute(router: IRouter { jest.clearAllMocks(); log.messages.length = 0; process.execArgv = ['--inheritted', '--exec', '--argv']; - process.env.FORCE_COLOR = process.env.FORCE_COLOR || '1'; currentProc = undefined; }); diff --git a/packages/kbn-cli-dev-mode/src/dev_server.ts b/packages/kbn-cli-dev-mode/src/dev_server.ts index bed1afe126c1..cf89d2c00d16 100644 --- a/packages/kbn-cli-dev-mode/src/dev_server.ts +++ b/packages/kbn-cli-dev-mode/src/dev_server.ts @@ -34,6 +34,7 @@ export interface Options { sigint$?: Rx.Observable; sigterm$?: Rx.Observable; mapLogLine?: DevServer['mapLogLine']; + forceColor?: boolean; } export class DevServer { @@ -50,6 +51,7 @@ export class DevServer { private readonly argv: string[]; private readonly gracefulTimeout: number; private readonly mapLogLine?: (line: string) => string | null; + private readonly forceColor: boolean; constructor(options: Options) { this.log = options.log; @@ -62,6 +64,7 @@ export class DevServer { this.sigint$ = options.sigint$ ?? Rx.fromEvent(process, 'SIGINT'); this.sigterm$ = options.sigterm$ ?? Rx.fromEvent(process, 'SIGTERM'); this.mapLogLine = options.mapLogLine; + this.forceColor = options.forceColor ?? !!process.stdout.isTTY; } isReady$() { @@ -141,8 +144,13 @@ export class DevServer { }) ); + const serverOptions = { + script: this.script, + argv: this.argv, + forceColor: this.forceColor, + }; const runServer = () => - usingServerProcess(this.script, this.argv, (proc) => { + usingServerProcess(serverOptions, (proc) => { this.phase$.next('starting'); this.ready$.next(false); diff --git a/packages/kbn-cli-dev-mode/src/using_server_process.ts b/packages/kbn-cli-dev-mode/src/using_server_process.ts index eb997295035d..690ba250cbcb 100644 --- a/packages/kbn-cli-dev-mode/src/using_server_process.ts +++ b/packages/kbn-cli-dev-mode/src/using_server_process.ts @@ -18,14 +18,19 @@ interface ProcResource extends Rx.Unsubscribable { unsubscribe(): void; } +interface Options { + script: string; + argv: string[]; + forceColor: boolean; +} + export function usingServerProcess( - script: string, - argv: string[], + options: Options, fn: (proc: execa.ExecaChildProcess) => Rx.Observable ) { return Rx.using( (): ProcResource => { - const proc = execa.node(script, argv, { + const proc = execa.node(options.script, options.argv, { stdio: 'pipe', nodeOptions: [ ...process.execArgv, @@ -36,7 +41,7 @@ export function usingServerProcess( NODE_OPTIONS: process.env.NODE_OPTIONS, isDevCliChild: 'true', ELASTIC_APM_SERVICE_NAME: 'kibana', - ...(process.stdout.isTTY ? { FORCE_COLOR: 'true' } : {}), + ...(options.forceColor ? { FORCE_COLOR: 'true' } : {}), }, }); diff --git a/packages/kbn-doc-links/src/get_doc_links.ts b/packages/kbn-doc-links/src/get_doc_links.ts index a4e788d7639e..a661707bfa56 100644 --- a/packages/kbn-doc-links/src/get_doc_links.ts +++ b/packages/kbn-doc-links/src/get_doc_links.ts @@ -387,6 +387,7 @@ export const getDocLinks = ({ kibanaBranch }: GetDocLinkOptions): DocLinks => { regressionEvaluation: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-dfa-regression.html#ml-dfanalytics-regression-evaluation`, classificationAucRoc: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-dfa-classification.html#ml-dfanalytics-class-aucroc`, setUpgradeMode: `${ELASTICSEARCH_DOCS}ml-set-upgrade-mode.html`, + trainedModels: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-trained-models.html`, }, transforms: { guide: `${ELASTICSEARCH_DOCS}transforms.html`, diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index 2540976c106f..98089f1f1c5f 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -122,7 +122,7 @@ pageLoadAssetSize: sessionView: 77750 cloudSecurityPosture: 19109 visTypeGauge: 24113 - unifiedSearch: 49195 + unifiedSearch: 104869 data: 454087 expressionXY: 26500 eventAnnotation: 19334 diff --git a/packages/kbn-securitysolution-autocomplete/src/field_value_match/index.tsx b/packages/kbn-securitysolution-autocomplete/src/field_value_match/index.tsx index 096058fcc0aa..bfede3a8fcc4 100644 --- a/packages/kbn-securitysolution-autocomplete/src/field_value_match/index.tsx +++ b/packages/kbn-securitysolution-autocomplete/src/field_value_match/index.tsx @@ -21,7 +21,7 @@ import { uniq } from 'lodash'; import { ListOperatorTypeEnum as OperatorTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; // TODO: I have to use any here for now, but once this is available below, we should use the correct types, https://github.com/elastic/kibana/issues/100715 -// import { AutocompleteStart } from '../../../../../../../src/plugins/data/public'; +// import { AutocompleteStart } from '../../../../../../../src/plugins/unified_search/public'; type AutocompleteStart = any; import * as i18n from '../translations'; diff --git a/packages/kbn-securitysolution-autocomplete/src/field_value_match_any/index.tsx b/packages/kbn-securitysolution-autocomplete/src/field_value_match_any/index.tsx index 5a36e155c548..8f5afbbc8662 100644 --- a/packages/kbn-securitysolution-autocomplete/src/field_value_match_any/index.tsx +++ b/packages/kbn-securitysolution-autocomplete/src/field_value_match_any/index.tsx @@ -13,7 +13,7 @@ import { ListOperatorTypeEnum as OperatorTypeEnum } from '@kbn/securitysolution- import { DataViewBase, DataViewFieldBase } from '@kbn/es-query'; // TODO: I have to use any here for now, but once this is available below, we should use the correct types, https://github.com/elastic/kibana/issues/100715 -// import { AutocompleteStart } from '../../../../../../../src/plugins/data/public'; +// import { AutocompleteStart } from '../../../../../../../src/plugins/unified_search/public'; type AutocompleteStart = any; import * as i18n from '../translations'; diff --git a/packages/kbn-securitysolution-autocomplete/src/field_value_wildcard/index.tsx b/packages/kbn-securitysolution-autocomplete/src/field_value_wildcard/index.tsx index 82b9cb029d8d..82d36aef8372 100644 --- a/packages/kbn-securitysolution-autocomplete/src/field_value_wildcard/index.tsx +++ b/packages/kbn-securitysolution-autocomplete/src/field_value_wildcard/index.tsx @@ -15,7 +15,7 @@ import { uniq } from 'lodash'; import { ListOperatorTypeEnum as OperatorTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; // TODO: I have to use any here for now, but once this is available below, we should use the correct types, https://github.com/elastic/kibana/issues/100715 -// import { AutocompleteStart } from '../../../../../../../src/plugins/data/public'; +// import { AutocompleteStart } from '../../../../../../../src/plugins/unified_search/public'; type AutocompleteStart = any; import * as i18n from '../translations'; diff --git a/packages/kbn-securitysolution-autocomplete/src/hooks/use_field_value_autocomplete/index.ts b/packages/kbn-securitysolution-autocomplete/src/hooks/use_field_value_autocomplete/index.ts index ca0868e5056a..1b2de4567fe2 100644 --- a/packages/kbn-securitysolution-autocomplete/src/hooks/use_field_value_autocomplete/index.ts +++ b/packages/kbn-securitysolution-autocomplete/src/hooks/use_field_value_autocomplete/index.ts @@ -12,7 +12,7 @@ import { ListOperatorTypeEnum as OperatorTypeEnum } from '@kbn/securitysolution- import { DataViewBase, DataViewFieldBase, getDataViewFieldSubtypeNested } from '@kbn/es-query'; // TODO: I have to use any here for now, but once this is available below, we should use the correct types, https://github.com/elastic/kibana/issues/100715 -// import { AutocompleteStart } from '../../../../../../../../src/plugins/data/public'; +// import { AutocompleteStart } from '../../../../../../../../src/plugins/unified_search/public'; type AutocompleteStart = any; interface FuncArgs { diff --git a/packages/kbn-utility-types/src/index.ts b/packages/kbn-utility-types/src/index.ts index 277b4a70e415..50741bbd9695 100644 --- a/packages/kbn-utility-types/src/index.ts +++ b/packages/kbn-utility-types/src/index.ts @@ -26,6 +26,13 @@ export type MaybePromise = T | Promise; */ export type ShallowPromise = T extends Promise ? Promise : Promise; +/** + * Unwrap all promise attributes of the given type + */ +export type AwaitedProperties = { + [K in keyof T]: Awaited; +}; + /** * Minimal interface for an object resembling an `Observable`. */ diff --git a/src/cli_encryption_keys/encryption_config.test.js b/src/cli_encryption_keys/encryption_config.test.js index 9629cfcafe78..dba1967f4f6b 100644 --- a/src/cli_encryption_keys/encryption_config.test.js +++ b/src/cli_encryption_keys/encryption_config.test.js @@ -14,7 +14,7 @@ describe('encryption key configuration', () => { let encryptionConfig = null; beforeEach(() => { - jest.spyOn(fs, 'readFileSync').mockReturnValue('xpack.security.encryptionKey: foo'); + jest.spyOn(fs, 'readFileSync').mockReturnValueOnce('xpack.security.encryptionKey: foo'); jest.spyOn(crypto, 'randomBytes').mockReturnValue('random-key'); encryptionConfig = new EncryptionConfig(); }); diff --git a/src/core/server/context/container/context.test.ts b/src/core/server/context/container/context.test.ts index 25fab2e3f0bf..17a04531dedd 100644 --- a/src/core/server/context/container/context.test.ts +++ b/src/core/server/context/container/context.test.ts @@ -7,7 +7,7 @@ */ import { ContextContainer } from './context'; -import { PluginOpaqueId } from '../..'; +import type { PluginOpaqueId, RequestHandlerContextBase } from '../..'; import { httpServerMock } from '../../http/http_server.mocks'; const pluginA = Symbol('pluginA'); @@ -22,7 +22,7 @@ const plugins: ReadonlyMap = new Map([ ]); const coreId = Symbol(); -interface MyContext { +interface MyContext extends RequestHandlerContextBase { core: any; core1: string; core2: number; @@ -32,31 +32,47 @@ interface MyContext { ctxFromD: object; } +type TestContext = T & RequestHandlerContextBase; + describe('ContextContainer', () => { - it('does not allow the same context to be registered twice', () => { - const contextContainer = new ContextContainer(plugins, coreId); - contextContainer.registerContext<{ ctxFromA: string; core: any }, 'ctxFromA'>( - coreId, - 'ctxFromA', - () => 'aString' - ); - - expect(() => - contextContainer.registerContext<{ ctxFromA: string; core: any }, 'ctxFromA'>( + describe('registerContext', () => { + it('throws an error if the same context is registered twice', () => { + const contextContainer = new ContextContainer(plugins, coreId); + contextContainer.registerContext, 'ctxFromA'>( coreId, 'ctxFromA', () => 'aString' - ) - ).toThrowErrorMatchingInlineSnapshot( - `"Context provider for ctxFromA has already been registered."` - ); - }); + ); + + expect(() => + contextContainer.registerContext, 'ctxFromA'>( + coreId, + 'ctxFromA', + () => 'aString' + ) + ).toThrowErrorMatchingInlineSnapshot( + `"Context provider for ctxFromA has already been registered."` + ); + }); + + it('throws an error if a `resolve` context is registered', () => { + const contextContainer = new ContextContainer(plugins, coreId); + expect(() => + contextContainer.registerContext, 'ctxFromA'>( + coreId, + // @ts-expect-error protected with typing too + 'resolve', + () => 'aString' + ) + ).toThrowErrorMatchingInlineSnapshot( + `"Cannot register a provider for resolve, it is a reserved keyword."` + ); + }); - describe('registerContext', () => { it('throws error if called with an unknown symbol', async () => { const contextContainer = new ContextContainer(plugins, coreId); await expect(() => - contextContainer.registerContext<{ ctxFromA: string; core: any }, 'ctxFromA'>( + contextContainer.registerContext, 'ctxFromA'>( Symbol('unknown'), 'ctxFromA', jest.fn() @@ -69,7 +85,7 @@ describe('ContextContainer', () => { it('reports a TS error if returned contract does not satisfy the Context interface', async () => { const contextContainer = new ContextContainer(plugins, coreId); await expect(() => - contextContainer.registerContext<{ ctxFromA: string; core: any }, 'ctxFromA'>( + contextContainer.registerContext, 'ctxFromA'>( pluginA, 'ctxFromA', // @ts-expect-error expected string, returned number @@ -82,7 +98,7 @@ describe('ContextContainer', () => { const contextContainer = new ContextContainer(plugins, coreId); await expect(() => // @ts-expect-error expects ctxFromB, but given ctxFromC - contextContainer.registerContext<{ ctxFromB: string; core: any }, 'ctxFromC'>( + contextContainer.registerContext, 'ctxFromC'>( pluginB, 'ctxFromC', async () => 1 @@ -92,39 +108,216 @@ describe('ContextContainer', () => { }); describe('context building', () => { + const resolveAllContexts = async (ctx: Record): Promise => { + const resolved = {} as Record; + for (const key of Object.getOwnPropertyNames(ctx)) { + if (key === 'resolve') { + continue; + } + resolved[key] = await ctx[key]; + } + return resolved; + }; + + it('lazily loads the providers when accessed', async () => { + const contextContainer = new ContextContainer(plugins, coreId); + + const core1provider = jest.fn().mockReturnValue('core1'); + const ctxFromAProvider = jest.fn().mockReturnValue('ctxFromA'); + + contextContainer.registerContext, 'core1'>( + coreId, + 'core1', + core1provider + ); + + contextContainer.registerContext, 'ctxFromA'>( + pluginA, + 'ctxFromA', + ctxFromAProvider + ); + + let context: any; + const rawHandler1 = jest.fn((ctx) => { + context = ctx; + return 'rawHandler1' as any; + }); + const handler1 = contextContainer.createHandler(pluginC, rawHandler1); + + const request = httpServerMock.createKibanaRequest(); + const response = httpServerMock.createResponseFactory(); + await handler1(request, response); + + expect(core1provider).not.toHaveBeenCalled(); + expect(ctxFromAProvider).not.toHaveBeenCalled(); + + await context!.core1; + + expect(core1provider).toHaveBeenCalledTimes(1); + expect(ctxFromAProvider).not.toHaveBeenCalled(); + + await context!.ctxFromA; + + expect(core1provider).toHaveBeenCalledTimes(1); + expect(ctxFromAProvider).toHaveBeenCalledTimes(1); + }); + + it(`does not eagerly loads a provider's dependencies`, async () => { + const contextContainer = new ContextContainer(plugins, coreId); + + const core1provider = jest.fn().mockReturnValue('core1'); + const ctxFromAProvider = jest.fn().mockReturnValue('ctxFromA'); + + contextContainer.registerContext, 'core1'>( + coreId, + 'core1', + core1provider + ); + + contextContainer.registerContext, 'ctxFromA'>( + pluginA, + 'ctxFromA', + ctxFromAProvider + ); + + let context: any; + const rawHandler1 = jest.fn((ctx) => { + context = ctx; + return 'rawHandler1' as any; + }); + const handler1 = contextContainer.createHandler(pluginC, rawHandler1); + + const request = httpServerMock.createKibanaRequest(); + const response = httpServerMock.createResponseFactory(); + await handler1(request, response); + + expect(core1provider).not.toHaveBeenCalled(); + expect(ctxFromAProvider).not.toHaveBeenCalled(); + + await context!.ctxFromA; + + expect(core1provider).not.toHaveBeenCalled(); + expect(ctxFromAProvider).toHaveBeenCalledTimes(1); + }); + + it(`allows to load a dependency from a provider`, async () => { + const contextContainer = new ContextContainer(plugins, coreId); + + const core1provider = jest.fn().mockReturnValue('core1'); + const ctxFromAProvider = jest.fn().mockImplementation(async (ctx: any) => { + const core1 = await ctx.core1; + return `${core1}-ctxFromA`; + }); + + contextContainer.registerContext, 'core1'>( + coreId, + 'core1', + core1provider + ); + + contextContainer.registerContext, 'ctxFromA'>( + pluginA, + 'ctxFromA', + ctxFromAProvider + ); + + let context: any; + const rawHandler1 = jest.fn((ctx) => { + context = ctx; + return 'rawHandler1' as any; + }); + const handler1 = contextContainer.createHandler(pluginC, rawHandler1); + + const request = httpServerMock.createKibanaRequest(); + const response = httpServerMock.createResponseFactory(); + await handler1(request, response); + + expect(core1provider).not.toHaveBeenCalled(); + expect(ctxFromAProvider).not.toHaveBeenCalled(); + + const contextValue = await context!.ctxFromA; + + expect(contextValue).toEqual('core1-ctxFromA'); + expect(core1provider).toHaveBeenCalledTimes(1); + expect(ctxFromAProvider).toHaveBeenCalledTimes(1); + }); + + it(`only calls a provider once and caches the returned value`, async () => { + const contextContainer = new ContextContainer(plugins, coreId); + + const core1provider = jest.fn().mockReturnValue('core1'); + const ctxFromAProvider = jest.fn().mockImplementation(async (ctx: any) => { + const core1 = await ctx.core1; + return `${core1}-ctxFromA`; + }); + + contextContainer.registerContext, 'core1'>( + coreId, + 'core1', + core1provider + ); + + contextContainer.registerContext, 'ctxFromA'>( + pluginA, + 'ctxFromA', + ctxFromAProvider + ); + + let context: any; + const rawHandler1 = jest.fn((ctx) => { + context = ctx; + return 'rawHandler1' as any; + }); + const handler1 = contextContainer.createHandler(pluginC, rawHandler1); + + const request = httpServerMock.createKibanaRequest(); + const response = httpServerMock.createResponseFactory(); + await handler1(request, response); + + expect(core1provider).not.toHaveBeenCalled(); + expect(ctxFromAProvider).not.toHaveBeenCalled(); + + await context!.core1; + await context!.ctxFromA; + await context!.core1; + + expect(core1provider).toHaveBeenCalledTimes(1); + expect(ctxFromAProvider).toHaveBeenCalledTimes(1); + }); + it('resolves dependencies', async () => { const contextContainer = new ContextContainer(plugins, coreId); - expect.assertions(8); - contextContainer.registerContext<{ core1: string; core: any }, 'core1'>( + expect.assertions(10); + contextContainer.registerContext, 'core1'>( coreId, 'core1', - (context) => { - expect(context).toEqual({}); + async (context) => { + expect(await resolveAllContexts(context)).toEqual({}); return 'core'; } ); - contextContainer.registerContext<{ ctxFromA: string; core: any }, 'ctxFromA'>( + contextContainer.registerContext, 'ctxFromA'>( pluginA, 'ctxFromA', - (context) => { - expect(context).toEqual({ core1: 'core' }); + async (context) => { + expect(await resolveAllContexts(context)).toEqual({ core1: 'core' }); return 'aString'; } ); - contextContainer.registerContext<{ ctxFromB: number; core: any }, 'ctxFromB'>( + contextContainer.registerContext, 'ctxFromB'>( pluginB, 'ctxFromB', - (context) => { - expect(context).toEqual({ core1: 'core', ctxFromA: 'aString' }); + async (context) => { + expect(await resolveAllContexts(context)).toEqual({ core1: 'core', ctxFromA: 'aString' }); return 299; } ); - contextContainer.registerContext<{ ctxFromC: boolean; core: any }, 'ctxFromC'>( + contextContainer.registerContext, 'ctxFromC'>( pluginC, 'ctxFromC', - (context) => { - expect(context).toEqual({ + async (context) => { + expect(await resolveAllContexts(context)).toEqual({ core1: 'core', ctxFromA: 'aString', ctxFromB: 299, @@ -132,19 +325,34 @@ describe('ContextContainer', () => { return false; } ); - contextContainer.registerContext<{ ctxFromD: {}; core: any }, 'ctxFromD'>( + contextContainer.registerContext, 'ctxFromD'>( pluginD, 'ctxFromD', - (context) => { - expect(context).toEqual({ core1: 'core' }); + async (context) => { + expect(await resolveAllContexts(context)).toEqual({ core1: 'core' }); return {}; } ); - const rawHandler1 = jest.fn(() => 'handler1' as any); + const rawHandler1 = jest.fn(async (context) => { + expect(await resolveAllContexts(context)).toEqual({ + core1: 'core', + ctxFromA: 'aString', + ctxFromB: 299, + ctxFromC: false, + }); + return 'handler1' as any; + }); const handler1 = contextContainer.createHandler(pluginC, rawHandler1); - const rawHandler2 = jest.fn(() => 'handler2' as any); + const rawHandler2 = jest.fn(async (context) => { + expect(await resolveAllContexts(context)).toEqual({ + core1: 'core', + ctxFromD: {}, + }); + return 'handler2' as any; + }); + const handler2 = contextContainer.createHandler(pluginD, rawHandler2); const request = httpServerMock.createKibanaRequest(); @@ -154,41 +362,25 @@ describe('ContextContainer', () => { await handler2(request, response); // Should have context from pluginC, its deps, and core - expect(rawHandler1).toHaveBeenCalledWith( - { - core1: 'core', - ctxFromA: 'aString', - ctxFromB: 299, - ctxFromC: false, - }, - request, - response - ); + expect(rawHandler1).toHaveBeenCalledWith(expect.any(Object), request, response); // Should have context from pluginD, and core - expect(rawHandler2).toHaveBeenCalledWith( - { - core1: 'core', - ctxFromD: {}, - }, - request, - response - ); + expect(rawHandler2).toHaveBeenCalledWith(expect.any(Object), request, response); }); it('exposes all core context to all providers regardless of registration order', async () => { - expect.assertions(4); + expect.assertions(5); const contextContainer = new ContextContainer(plugins, coreId); contextContainer - .registerContext(pluginA, 'ctxFromA', (context) => { - expect(context).toEqual({ core1: 'core', core2: 101 }); - return `aString ${context.core1} ${context.core2}`; + .registerContext(pluginA, 'ctxFromA', async (context) => { + expect(await resolveAllContexts(context)).toEqual({ core1: 'core', core2: 101 }); + return `aString ${await context.core1} ${await context.core2}`; }) .registerContext(coreId, 'core1', () => 'core') .registerContext(coreId, 'core2', () => 101) - .registerContext(pluginB, 'ctxFromB', (context) => { - expect(context).toEqual({ + .registerContext(pluginB, 'ctxFromB', async (context) => { + expect(await resolveAllContexts(context)).toEqual({ core1: 'core', core2: 101, ctxFromA: 'aString core 101', @@ -196,40 +388,45 @@ describe('ContextContainer', () => { return 277; }); - const rawHandler1 = jest.fn(() => 'handler1' as any); + const rawHandler1 = jest.fn(async (context) => { + expect(await resolveAllContexts(context)).toEqual({ + core1: 'core', + core2: 101, + ctxFromA: 'aString core 101', + ctxFromB: 277, + }); + return 'handler1' as any; + }); const handler1 = contextContainer.createHandler(pluginB, rawHandler1); const request = httpServerMock.createKibanaRequest(); const response = httpServerMock.createResponseFactory(); expect(await handler1(request, response)).toEqual('handler1'); - expect(rawHandler1).toHaveBeenCalledWith( - { - core1: 'core', - core2: 101, - ctxFromA: 'aString core 101', - ctxFromB: 277, - }, - request, - response - ); + expect(rawHandler1).toHaveBeenCalledWith(expect.any(Object), request, response); }); it('exposes all core context to core providers', async () => { - expect.assertions(4); + expect.assertions(5); const contextContainer = new ContextContainer(plugins, coreId); contextContainer - .registerContext(coreId, 'core1', (context) => { - expect(context).toEqual({}); + .registerContext(coreId, 'core1', async (context) => { + expect(await resolveAllContexts(context)).toEqual({}); return 'core'; }) - .registerContext(coreId, 'core2', (context) => { - expect(context).toEqual({ core1: 'core' }); + .registerContext(coreId, 'core2', async (context) => { + expect(await resolveAllContexts(context)).toEqual({ core1: 'core' }); return 101; }); - const rawHandler1 = jest.fn(() => 'handler1' as any); + const rawHandler1 = jest.fn(async (context) => { + expect(await resolveAllContexts(context)).toEqual({ + core1: 'core', + core2: 101, + }); + return 'handler1' as any; + }); const handler1 = contextContainer.createHandler(pluginA, rawHandler1); const request = httpServerMock.createKibanaRequest(); @@ -237,14 +434,7 @@ describe('ContextContainer', () => { expect(await handler1(request, response)).toEqual('handler1'); // If no context is registered for pluginA, only core contexts should be exposed - expect(rawHandler1).toHaveBeenCalledWith( - { - core1: 'core', - core2: 101, - }, - request, - response - ); + expect(rawHandler1).toHaveBeenCalledWith(expect.any(Object), request, response); }); it('does not expose plugin contexts to core handler', async () => { @@ -254,24 +444,24 @@ describe('ContextContainer', () => { .registerContext(coreId, 'core1', (context) => 'core') .registerContext(pluginA, 'ctxFromA', (context) => 'aString'); - const rawHandler1 = jest.fn(() => 'handler1' as any); + const rawHandler1 = jest.fn(async (context) => { + // pluginA context should not be present in a core handler + expect(await resolveAllContexts(context)).toEqual({ + core1: 'core', + }); + return 'handler1' as any; + }); const handler1 = contextContainer.createHandler(coreId, rawHandler1); const request = httpServerMock.createKibanaRequest(); const response = httpServerMock.createResponseFactory(); expect(await handler1(request, response)).toEqual('handler1'); - // pluginA context should not be present in a core handler - expect(rawHandler1).toHaveBeenCalledWith( - { - core1: 'core', - }, - request, - response - ); + + expect(rawHandler1).toHaveBeenCalledWith(expect.any(Object), request, response); }); it('passes additional arguments to providers', async () => { - expect.assertions(6); + expect.assertions(7); const contextContainer = new ContextContainer(plugins, coreId); const request = httpServerMock.createKibanaRequest(); @@ -292,19 +482,102 @@ describe('ContextContainer', () => { } ); - const rawHandler1 = jest.fn(() => 'handler1' as any); + const rawHandler1 = jest.fn(async (context) => { + expect(await resolveAllContexts(context)).toEqual({ + core1: 'core', + ctxFromB: 77, + }); + return 'handler1' as any; + }); const handler1 = contextContainer.createHandler(pluginD, rawHandler1); expect(await handler1(request, response)).toEqual('handler1'); - expect(rawHandler1).toHaveBeenCalledWith( - { - core1: 'core', - ctxFromB: 77, - }, - request, - response - ); + expect(rawHandler1).toHaveBeenCalledWith(expect.any(Object), request, response); + }); + + describe('#resolve', () => { + it('resolves dependencies', async () => { + const contextContainer = new ContextContainer(plugins, coreId); + expect.assertions(10); + contextContainer.registerContext(coreId, 'core1', async (context) => { + expect(await context.resolve([])).toEqual({}); + return 'core'; + }); + + contextContainer.registerContext( + pluginA, + 'ctxFromA', + async (context) => { + expect(await context.resolve(['core1'])).toEqual({ core1: 'core' }); + return 'aString'; + } + ); + contextContainer.registerContext( + pluginB, + 'ctxFromB', + async (context) => { + expect(await context.resolve(['core1', 'ctxFromA'])).toEqual({ + core1: 'core', + ctxFromA: 'aString', + }); + return 299; + } + ); + contextContainer.registerContext( + pluginC, + 'ctxFromC', + async (context) => { + expect(await context.resolve(['core1', 'ctxFromA', 'ctxFromB'])).toEqual({ + core1: 'core', + ctxFromA: 'aString', + ctxFromB: 299, + }); + return false; + } + ); + contextContainer.registerContext( + pluginD, + 'ctxFromD', + async (context) => { + expect(await context.resolve(['core1'])).toEqual({ core1: 'core' }); + return {}; + } + ); + + const rawHandler1 = jest.fn(async (context) => { + expect(await context.resolve(['core1', 'ctxFromA', 'ctxFromB', 'ctxFromC'])).toEqual({ + core1: 'core', + ctxFromA: 'aString', + ctxFromB: 299, + ctxFromC: false, + }); + return 'handler1' as any; + }); + const handler1 = contextContainer.createHandler(pluginC, rawHandler1); + + const rawHandler2 = jest.fn(async (context) => { + expect(await context.resolve(['core1', 'ctxFromD'])).toEqual({ + core1: 'core', + ctxFromD: {}, + }); + return 'handler2' as any; + }); + + const handler2 = contextContainer.createHandler(pluginD, rawHandler2); + + const request = httpServerMock.createKibanaRequest(); + const response = httpServerMock.createResponseFactory(); + + await handler1(request, response); + await handler2(request, response); + + // Should have context from pluginC, its deps, and core + expect(rawHandler1).toHaveBeenCalledWith(expect.any(Object), request, response); + + // Should have context from pluginD, and core + expect(rawHandler2).toHaveBeenCalledWith(expect.any(Object), request, response); + }); }); }); @@ -337,7 +610,11 @@ describe('ContextContainer', () => { const request = httpServerMock.createKibanaRequest(); const response = httpServerMock.createResponseFactory(); await handler1(request, response); - expect(rawHandler1).toHaveBeenCalledWith({}, request, response); + expect(rawHandler1).toHaveBeenCalledWith( + { resolve: expect.any(Function) }, + request, + response + ); }); }); }); diff --git a/src/core/server/context/container/context.ts b/src/core/server/context/container/context.ts index 2650c2185360..4bc9a70a7afb 100644 --- a/src/core/server/context/container/context.ts +++ b/src/core/server/context/container/context.ts @@ -7,8 +7,7 @@ */ import { flatten } from 'lodash'; -import { ShallowPromise } from '@kbn/utility-types'; -import { pick } from 'lodash'; +import { ShallowPromise, MaybePromise } from '@kbn/utility-types'; import type { CoreId, PluginOpaqueId, RequestHandler, RequestHandlerContext } from '../..'; /** @@ -31,7 +30,7 @@ export type IContextProvider< // context.core will always be available, but plugin contexts are typed as optional context: Omit, ...rest: HandlerParameters -) => Promise | Context[ContextName]; +) => MaybePromise>; /** * A function that accepts a context object and an optional number of additional arguments. Used for the generic types @@ -202,6 +201,9 @@ export class ContextContainer implements IContextContainer { provider: IContextProvider ): this => { const contextName = name as string; + if (contextName === 'resolve') { + throw new Error(`Cannot register a provider for ${contextName}, it is a reserved keyword.`); + } if (this.contextProviders.has(contextName)) { throw new Error(`Context provider for ${contextName} has already been registered.`); } @@ -224,36 +226,51 @@ export class ContextContainer implements IContextContainer { } return (async (...args: HandlerParameters) => { - const context = await this.buildContext(source, ...args); + const context = this.buildContext(source, ...args); return handler(context, ...args); }) as ( ...args: HandlerParameters ) => ShallowPromise>; }; - private async buildContext( + private buildContext( source: symbol, ...contextArgs: HandlerParameters - ): Promise> { + ): HandlerContextType { const contextsToBuild = new Set(this.getContextNamesForSource(source)); + const builtContextPromises: Record> = {}; + + const builtContext = {} as HandlerContextType; + (builtContext as unknown as RequestHandlerContext).resolve = async (keys) => { + const resolved = await Promise.all( + keys.map(async (key) => { + return [key, await builtContext[key]]; + }) + ); + return Object.fromEntries(resolved); + }; return [...this.contextProviders] .sort(sortByCoreFirst(this.coreId)) .filter(([contextName]) => contextsToBuild.has(contextName)) - .reduce(async (contextPromise, [contextName, { provider, source: providerSource }]) => { - const resolvedContext = await contextPromise; - - // For the next provider, only expose the context available based on the dependencies of the plugin that - // registered that provider. - const exposedContext = pick(resolvedContext, [ - ...this.getContextNamesForSource(providerSource), - ]); + .reduce((contextAccessors, [contextName, { provider, source: providerSource }]) => { + const exposedContext = createExposedContext({ + currentContextName: contextName, + exposedContextNames: [...this.getContextNamesForSource(providerSource)], + contextAccessors, + }); + Object.defineProperty(contextAccessors, contextName, { + get: async () => { + const contextKey = contextName as keyof HandlerContextType; + if (!builtContextPromises[contextKey]) { + builtContextPromises[contextKey] = provider(exposedContext, ...contextArgs); + } + return await builtContextPromises[contextKey]; + }, + }); - return { - ...resolvedContext, - [contextName]: await provider(exposedContext, ...contextArgs), - }; - }, Promise.resolve({}) as Promise>); + return contextAccessors; + }, builtContext); } private getContextNamesForSource(source: symbol): ReadonlySet { @@ -299,3 +316,28 @@ const sortByCoreFirst = return rightProvider.source === coreId ? 1 : 0; } }; + +const createExposedContext = ({ + currentContextName, + exposedContextNames, + contextAccessors, +}: { + currentContextName: string; + exposedContextNames: string[]; + contextAccessors: Partial>; +}) => { + const exposedContext: Partial> = {}; + exposedContext.resolve = contextAccessors.resolve; + + for (const contextName of exposedContextNames) { + if (contextName === currentContextName) { + continue; + } + const descriptor = Object.getOwnPropertyDescriptor(contextAccessors, contextName); + if (descriptor) { + Object.defineProperty(exposedContext, contextName, descriptor); + } + } + + return exposedContext; +}; diff --git a/src/core/server/core_app/core_app.ts b/src/core/server/core_app/core_app.ts index bd7de6b20226..28c52775b2d2 100644 --- a/src/core/server/core_app/core_app.ts +++ b/src/core/server/core_app/core_app.ts @@ -89,7 +89,8 @@ export class CoreApp { const resources = coreSetup.httpResources.createRegistrar(router); router.get({ path: '/', validate: false }, async (context, req, res) => { - const defaultRoute = await context.core.uiSettings.client.get('defaultRoute'); + const { uiSettings } = await context.core; + const defaultRoute = await uiSettings.client.get('defaultRoute'); const basePath = httpSetup.basePath.get(req); const url = `${basePath}${defaultRoute}`; diff --git a/src/core/server/deprecations/routes/get.ts b/src/core/server/deprecations/routes/get.ts index cce9369db617..88965505488f 100644 --- a/src/core/server/deprecations/routes/get.ts +++ b/src/core/server/deprecations/routes/get.ts @@ -15,7 +15,7 @@ export const registerGetRoute = (router: IRouter) => { validate: false, }, async (context, req, res) => { - const deprecationsClient = context.core.deprecations.client; + const deprecationsClient = (await context.core).deprecations.client; const body: DeprecationsGetResponse = { deprecations: await deprecationsClient.getAllDeprecations(), diff --git a/src/core/server/execution_context/integration_tests/tracing.test.ts b/src/core/server/execution_context/integration_tests/tracing.test.ts index 15813529c358..2634c8183373 100644 --- a/src/core/server/execution_context/integration_tests/tracing.test.ts +++ b/src/core/server/execution_context/integration_tests/tracing.test.ts @@ -65,10 +65,8 @@ describe('trace', () => { const router = createRouter(''); router.get({ path: '/execution-context', validate: false }, async (context, req, res) => { - const { headers } = await context.core.elasticsearch.client.asInternalUser.ping( - {}, - { meta: true } - ); + const esClient = (await context.core).elasticsearch.client; + const { headers } = await esClient.asInternalUser.ping({}, { meta: true }); return res.ok({ body: headers || {} }); }); @@ -90,10 +88,8 @@ describe('trace', () => { const router = createRouter(''); router.get({ path: '/execution-context', validate: false }, async (context, req, res) => { - const { headers } = await context.core.elasticsearch.client.asCurrentUser.ping( - {}, - { meta: true } - ); + const esClient = (await context.core).elasticsearch.client; + const { headers } = await esClient.asCurrentUser.ping({}, { meta: true }); return res.ok({ body: headers || {} }); }); @@ -115,10 +111,8 @@ describe('trace', () => { const router = createRouter(''); router.get({ path: '/execution-context', validate: false }, async (context, req, res) => { - const { headers } = await context.core.elasticsearch.client.asInternalUser.ping( - {}, - { meta: true } - ); + const esClient = (await context.core).elasticsearch.client; + const { headers } = await esClient.asInternalUser.ping({}, { meta: true }); return res.ok({ body: headers || {} }); }); @@ -136,10 +130,8 @@ describe('trace', () => { const router = createRouter(''); router.get({ path: '/execution-context', validate: false }, async (context, req, res) => { - const { headers } = await context.core.elasticsearch.client.asCurrentUser.ping( - {}, - { meta: true } - ); + const esClient = (await context.core).elasticsearch.client; + const { headers } = await esClient.asCurrentUser.ping({}, { meta: true }); return res.ok({ body: headers || {} }); }); @@ -157,7 +149,8 @@ describe('trace', () => { const router = createRouter(''); router.get({ path: '/execution-context', validate: false }, async (context, req, res) => { - const { headers } = await context.core.elasticsearch.client.asInternalUser.ping( + const esClient = (await context.core).elasticsearch.client; + const { headers } = await esClient.asInternalUser.ping( {}, { opaqueId: 'new-opaque-id', @@ -203,10 +196,8 @@ describe('trace', () => { const router = createRouter(''); router.get({ path: '/execution-context', validate: false }, async (context, req, res) => { - const { headers } = await context.core.elasticsearch.client.asCurrentUser.ping( - {}, - { meta: true } - ); + const esClient = (await context.core).elasticsearch.client; + const { headers } = await esClient.asCurrentUser.ping({}, { meta: true }); return res.ok({ body: headers || {} }); }); @@ -229,10 +220,8 @@ describe('trace', () => { const router = createRouter(''); router.get({ path: '/execution-context', validate: false }, async (context, req, res) => { executionContext.set(parentContext); - const { headers } = await context.core.elasticsearch.client.asCurrentUser.ping( - {}, - { meta: true } - ); + const esClient = (await context.core).elasticsearch.client; + const { headers } = await esClient.asCurrentUser.ping({}, { meta: true }); return res.ok({ body: { context: executionContext.get()?.toJSON(), @@ -345,10 +334,8 @@ describe('trace', () => { router.get({ path: '/execution-context', validate: false }, async (context, req, res) => { executionContext.set(parentContext); await delay(id-- * 100); - const { headers } = await context.core.elasticsearch.client.asCurrentUser.ping( - {}, - { meta: true } - ); + const esClient = (await context.core).elasticsearch.client; + const { headers } = await esClient.asCurrentUser.ping({}, { meta: true }); return res.ok({ body: headers || {} }); }); @@ -397,10 +384,8 @@ describe('trace', () => { const router = createRouter(''); router.get({ path: '/execution-context', validate: false }, async (context, req, res) => { - const { headers } = await context.core.elasticsearch.client.asCurrentUser.ping( - {}, - { meta: true } - ); + const esClient = (await context.core).elasticsearch.client; + const { headers } = await esClient.asCurrentUser.ping({}, { meta: true }); return res.ok({ body: headers || {} }); }); @@ -487,10 +472,8 @@ describe('trace', () => { const router = createRouter(''); router.get({ path: '/execution-context', validate: false }, async (context, req, res) => { - const { headers } = await context.core.elasticsearch.client.asCurrentUser.ping( - {}, - { meta: true } - ); + const esClient = (await context.core).elasticsearch.client; + const { headers } = await esClient.asCurrentUser.ping({}, { meta: true }); return res.ok({ body: headers || {} }); }); @@ -511,10 +494,8 @@ describe('trace', () => { const router = createRouter(''); router.get({ path: '/execution-context', validate: false }, async (context, req, res) => { - const { headers } = await context.core.elasticsearch.client.asInternalUser.ping( - {}, - { meta: true } - ); + const esClient = (await context.core).elasticsearch.client; + const { headers } = await esClient.asInternalUser.ping({}, { meta: true }); return res.ok({ body: headers || {} }); }); @@ -535,10 +516,8 @@ describe('trace', () => { const router = createRouter(''); router.get({ path: '/execution-context', validate: false }, async (context, req, res) => { - const { headers } = await context.core.elasticsearch.client.asCurrentUser.ping( - {}, - { meta: true } - ); + const esClient = (await context.core).elasticsearch.client; + const { headers } = await esClient.asCurrentUser.ping({}, { meta: true }); return res.ok({ body: headers || {} }); }); @@ -561,10 +540,8 @@ describe('trace', () => { const router = createRouter(''); router.get({ path: '/execution-context', validate: false }, async (context, req, res) => { executionContext.set(parentContext); - const { headers } = await context.core.elasticsearch.client.asCurrentUser.ping( - {}, - { meta: true } - ); + const esClient = (await context.core).elasticsearch.client; + const { headers } = await esClient.asCurrentUser.ping({}, { meta: true }); return res.ok({ body: headers || {} }); }); @@ -593,10 +570,8 @@ describe('trace', () => { }; router.get({ path: '/execution-context', validate: false }, async (context, req, res) => { executionContext.set(ctx); - const { headers } = await context.core.elasticsearch.client.asCurrentUser.ping( - {}, - { meta: true } - ); + const esClient = (await context.core).elasticsearch.client; + const { headers } = await esClient.asCurrentUser.ping({}, { meta: true }); return res.ok({ body: headers || {} }); }); @@ -663,9 +638,10 @@ describe('trace', () => { description: 'new-description', }; router.get({ path: '/execution-context', validate: false }, async (context, req, res) => { - const { headers } = await executionContext.withContext(newContext, () => - context.core.elasticsearch.client.asCurrentUser.ping({}, { meta: true }) - ); + const esClient = (await context.core).elasticsearch.client; + const { headers } = await executionContext.withContext(newContext, () => { + return esClient.asCurrentUser.ping({}, { meta: true }); + }); return res.ok({ body: headers || {} }); }); diff --git a/src/core/server/http/types.ts b/src/core/server/http/types.ts index e6d951410779..27f240af48e0 100644 --- a/src/core/server/http/types.ts +++ b/src/core/server/http/types.ts @@ -366,7 +366,7 @@ export interface HttpServiceSetup { */ registerRouteHandlerContext: < Context extends RequestHandlerContext, - ContextName extends keyof Context + ContextName extends keyof Omit >( contextName: ContextName, provider: RequestHandlerContextProvider @@ -393,7 +393,7 @@ export interface InternalHttpServiceSetup authRequestHeaders: IAuthHeadersStorage; registerRouteHandlerContext: < Context extends RequestHandlerContext, - ContextName extends keyof Context + ContextName extends keyof Omit >( pluginOpaqueId: PluginOpaqueId, contextName: ContextName, diff --git a/src/core/server/http_resources/http_resources_service.test.ts b/src/core/server/http_resources/http_resources_service.test.ts index 0a661f25d314..c74fde4d2e2a 100644 --- a/src/core/server/http_resources/http_resources_service.test.ts +++ b/src/core/server/http_resources/http_resources_service.test.ts @@ -27,7 +27,7 @@ describe('HttpResources service', () => { let setupDeps: SetupDeps; let router: jest.Mocked; const kibanaRequest = httpServerMock.createKibanaRequest(); - const context = { core: coreMock.createRequestHandlerContext() }; + const context = coreMock.createCustomRequestHandlerContext({}); const apmConfig = { mockApmConfig: true }; beforeEach(() => { @@ -71,7 +71,7 @@ describe('HttpResources service', () => { await routeHandler(context, kibanaRequest, responseFactory); expect(getDeps().rendering.render).toHaveBeenCalledWith( kibanaRequest, - context.core.uiSettings.client, + (await context.core).uiSettings.client, { isAnonymousPage: false, vars: { @@ -117,7 +117,7 @@ describe('HttpResources service', () => { await routeHandler(context, kibanaRequest, responseFactory); expect(getDeps().rendering.render).toHaveBeenCalledWith( kibanaRequest, - context.core.uiSettings.client, + (await context.core).uiSettings.client, { isAnonymousPage: true, vars: { diff --git a/src/core/server/http_resources/http_resources_service.ts b/src/core/server/http_resources/http_resources_service.ts index 692b248752df..2d49cb17cf4c 100644 --- a/src/core/server/http_resources/http_resources_service.ts +++ b/src/core/server/http_resources/http_resources_service.ts @@ -93,7 +93,8 @@ export class HttpResourcesService implements CoreService>( + parts: T[] + ) => Promise>>; +} + /** - * Plugin specific context passed to a route handler. + * Base context passed to a route handler. + * + * @public + */ +export interface RequestHandlerContext extends RequestHandlerContextBase { + core: Promise; +} + +/** @public */ +export type CustomRequestHandlerContext = RequestHandlerContext & { + [Key in keyof T]: T[Key] extends Promise ? T[Key] : Promise; +}; + +/** + * The `core` context provided to route handler. * * Provides the following clients and services: * - {@link SavedObjectsClient | savedObjects.client} - Saved Objects client @@ -477,27 +509,24 @@ export { TelemetryCounterType } from './analytics'; * data client which uses the credentials of the incoming request * - {@link IUiSettingsClient | uiSettings.client} - uiSettings client * which uses the credentials of the incoming request - * * @public */ -export interface RequestHandlerContext { - core: { - savedObjects: { - client: SavedObjectsClientContract; - typeRegistry: ISavedObjectTypeRegistry; - getClient: (options?: SavedObjectsClientProviderOptions) => SavedObjectsClientContract; - getExporter: (client: SavedObjectsClientContract) => ISavedObjectsExporter; - getImporter: (client: SavedObjectsClientContract) => ISavedObjectsImporter; - }; - elasticsearch: { - client: IScopedClusterClient; - }; - uiSettings: { - client: IUiSettingsClient; - }; - deprecations: { - client: DeprecationsClient; - }; +export interface CoreRequestHandlerContext { + savedObjects: { + client: SavedObjectsClientContract; + typeRegistry: ISavedObjectTypeRegistry; + getClient: (options?: SavedObjectsClientProviderOptions) => SavedObjectsClientContract; + getExporter: (client: SavedObjectsClientContract) => ISavedObjectsExporter; + getImporter: (client: SavedObjectsClientContract) => ISavedObjectsImporter; + }; + elasticsearch: { + client: IScopedClusterClient; + }; + uiSettings: { + client: IUiSettingsClient; + }; + deprecations: { + client: DeprecationsClient; }; } diff --git a/src/core/server/mocks.ts b/src/core/server/mocks.ts index 0e5821bcc1c2..cb7010808fe3 100644 --- a/src/core/server/mocks.ts +++ b/src/core/server/mocks.ts @@ -9,6 +9,7 @@ import { of } from 'rxjs'; import { duration } from 'moment'; import { ByteSizeValue } from '@kbn/config-schema'; +import { isPromise } from '@kbn/std'; import type { MockedKeys } from '@kbn/utility-types/jest'; import type { PluginInitializerContext, @@ -66,7 +67,11 @@ export { executionContextServiceMock } from './execution_context/execution_conte export { docLinksServiceMock } from './doc_links/doc_links_service.mock'; export { analyticsServiceMock } from './analytics/analytics_service.mock'; -export type { ElasticsearchClientMock } from './elasticsearch/client/mocks'; +export type { + ElasticsearchClientMock, + ClusterClientMock, + ScopedClusterClientMock, +} from './elasticsearch/client/mocks'; type MockedPluginInitializerConfig = jest.Mocked['config']>; @@ -280,6 +285,40 @@ function createCoreRequestHandlerContextMock() { }; } +export type CustomRequestHandlerMock = { + core: Promise>; + resolve: jest.MockedFunction; +} & { + [Key in keyof T]: T[Key] extends Promise ? T[Key] : Promise; +}; + +const createCustomRequestHandlerContextMock = (contextParts: T): CustomRequestHandlerMock => { + const mock = Object.entries(contextParts).reduce( + (context, [key, value]) => { + // @ts-expect-error type matching from inferred types is hard + context[key] = isPromise(value) ? value : Promise.resolve(value); + return context; + }, + { + core: Promise.resolve(createCoreRequestHandlerContextMock()), + } as CustomRequestHandlerMock + ); + + mock.resolve = jest.fn().mockImplementation(async () => { + const resolved = {}; + for (const propName of Object.keys(mock)) { + if (propName === 'resolve') { + continue; + } + // @ts-expect-error type matching from inferred types is hard + resolved[propName] = await mock[propName]; + } + return resolved; + }); + + return mock; +}; + export const coreMock = { createPreboot: createCorePrebootMock, createSetup: createCoreSetupMock, @@ -289,4 +328,5 @@ export const coreMock = { createInternalStart: createInternalCoreStartMock, createPluginInitializerContext: pluginInitializerContextMock, createRequestHandlerContext: createCoreRequestHandlerContextMock, + createCustomRequestHandlerContext: createCustomRequestHandlerContextMock, }; diff --git a/src/core/server/plugins/plugin_context.ts b/src/core/server/plugins/plugin_context.ts index 1566fd5bbe07..81e3b3a107c7 100644 --- a/src/core/server/plugins/plugin_context.ts +++ b/src/core/server/plugins/plugin_context.ts @@ -185,7 +185,7 @@ export function createPluginSetupContext( createCookieSessionStorageFactory: deps.http.createCookieSessionStorageFactory, registerRouteHandlerContext: < Context extends RequestHandlerContext, - ContextName extends keyof Context + ContextName extends keyof Omit >( contextName: ContextName, provider: RequestHandlerContextProvider diff --git a/src/core/server/rendering/bootstrap/register_bootstrap_route.ts b/src/core/server/rendering/bootstrap/register_bootstrap_route.ts index 6be537dce198..06f4100a0157 100644 --- a/src/core/server/rendering/bootstrap/register_bootstrap_route.ts +++ b/src/core/server/rendering/bootstrap/register_bootstrap_route.ts @@ -25,7 +25,7 @@ export const registerBootstrapRoute = ({ validate: false, }, async (ctx, req, res) => { - const uiSettingsClient = ctx.core.uiSettings.client; + const uiSettingsClient = (await ctx.core).uiSettings.client; const { body, etag } = await renderer({ uiSettingsClient, request: req }); return res.ok({ @@ -48,7 +48,7 @@ export const registerBootstrapRoute = ({ validate: false, }, async (ctx, req, res) => { - const uiSettingsClient = ctx.core.uiSettings.client; + const uiSettingsClient = (await ctx.core).uiSettings.client; const { body, etag } = await renderer({ uiSettingsClient, request: req, diff --git a/src/core/server/saved_objects/routes/bulk_create.ts b/src/core/server/saved_objects/routes/bulk_create.ts index f8438a70d041..b72cd6715fa1 100644 --- a/src/core/server/saved_objects/routes/bulk_create.ts +++ b/src/core/server/saved_objects/routes/bulk_create.ts @@ -51,7 +51,8 @@ export const registerBulkCreateRoute = (router: IRouter, { coreUsageData }: Rout const usageStatsClient = coreUsageData.getClient(); usageStatsClient.incrementSavedObjectsBulkCreate({ request: req }).catch(() => {}); - const result = await context.core.savedObjects.client.bulkCreate(req.body, { overwrite }); + const { savedObjects } = await context.core; + const result = await savedObjects.client.bulkCreate(req.body, { overwrite }); return res.ok({ body: result }); }) ); diff --git a/src/core/server/saved_objects/routes/bulk_get.ts b/src/core/server/saved_objects/routes/bulk_get.ts index cffa69b06f4e..87b6a604ac6c 100644 --- a/src/core/server/saved_objects/routes/bulk_get.ts +++ b/src/core/server/saved_objects/routes/bulk_get.ts @@ -34,7 +34,8 @@ export const registerBulkGetRoute = (router: IRouter, { coreUsageData }: RouteDe const usageStatsClient = coreUsageData.getClient(); usageStatsClient.incrementSavedObjectsBulkGet({ request: req }).catch(() => {}); - const result = await context.core.savedObjects.client.bulkGet(req.body); + const { savedObjects } = await context.core; + const result = await savedObjects.client.bulkGet(req.body); return res.ok({ body: result }); }) ); diff --git a/src/core/server/saved_objects/routes/bulk_resolve.ts b/src/core/server/saved_objects/routes/bulk_resolve.ts index 493f76a2c497..5754ec180541 100644 --- a/src/core/server/saved_objects/routes/bulk_resolve.ts +++ b/src/core/server/saved_objects/routes/bulk_resolve.ts @@ -32,7 +32,8 @@ export const registerBulkResolveRoute = (router: IRouter, { coreUsageData }: Rou const usageStatsClient = coreUsageData.getClient(); usageStatsClient.incrementSavedObjectsBulkResolve({ request: req }).catch(() => {}); - const result = await context.core.savedObjects.client.bulkResolve(req.body); + const { savedObjects } = await context.core; + const result = await savedObjects.client.bulkResolve(req.body); return res.ok({ body: result }); }) ); diff --git a/src/core/server/saved_objects/routes/bulk_update.ts b/src/core/server/saved_objects/routes/bulk_update.ts index 277673971dab..961b8349d174 100644 --- a/src/core/server/saved_objects/routes/bulk_update.ts +++ b/src/core/server/saved_objects/routes/bulk_update.ts @@ -44,7 +44,8 @@ export const registerBulkUpdateRoute = (router: IRouter, { coreUsageData }: Rout const usageStatsClient = coreUsageData.getClient(); usageStatsClient.incrementSavedObjectsBulkUpdate({ request: req }).catch(() => {}); - const savedObject = await context.core.savedObjects.client.bulkUpdate(req.body); + const { savedObjects } = await context.core; + const savedObject = await savedObjects.client.bulkUpdate(req.body); return res.ok({ body: savedObject }); }) ); diff --git a/src/core/server/saved_objects/routes/create.ts b/src/core/server/saved_objects/routes/create.ts index 3e287e91fec8..eb3938db0a9c 100644 --- a/src/core/server/saved_objects/routes/create.ts +++ b/src/core/server/saved_objects/routes/create.ts @@ -61,7 +61,8 @@ export const registerCreateRoute = (router: IRouter, { coreUsageData }: RouteDep references, initialNamespaces, }; - const result = await context.core.savedObjects.client.create(type, attributes, options); + const { savedObjects } = await context.core; + const result = await savedObjects.client.create(type, attributes, options); return res.ok({ body: result }); }) ); diff --git a/src/core/server/saved_objects/routes/delete.ts b/src/core/server/saved_objects/routes/delete.ts index e8404ba7fc8c..5c239a55a692 100644 --- a/src/core/server/saved_objects/routes/delete.ts +++ b/src/core/server/saved_objects/routes/delete.ts @@ -32,7 +32,7 @@ export const registerDeleteRoute = (router: IRouter, { coreUsageData }: RouteDep catchAndReturnBoomErrors(async (context, req, res) => { const { type, id } = req.params; const { force } = req.query; - const { getClient } = context.core.savedObjects; + const { getClient } = (await context.core).savedObjects; const usageStatsClient = coreUsageData.getClient(); usageStatsClient.incrementSavedObjectsDelete({ request: req }).catch(() => {}); diff --git a/src/core/server/saved_objects/routes/deprecations/delete_unknown_types.ts b/src/core/server/saved_objects/routes/deprecations/delete_unknown_types.ts index 97100980a37b..a30da9723b91 100644 --- a/src/core/server/saved_objects/routes/deprecations/delete_unknown_types.ts +++ b/src/core/server/saved_objects/routes/deprecations/delete_unknown_types.ts @@ -25,9 +25,10 @@ export const registerDeleteUnknownTypesRoute = ( validate: false, }, catchAndReturnBoomErrors(async (context, req, res) => { + const { elasticsearch, savedObjects } = await context.core; await deleteUnknownTypeObjects({ - esClient: context.core.elasticsearch.client, - typeRegistry: context.core.savedObjects.typeRegistry, + esClient: elasticsearch.client, + typeRegistry: savedObjects.typeRegistry, kibanaIndex, kibanaVersion, }); diff --git a/src/core/server/saved_objects/routes/export.ts b/src/core/server/saved_objects/routes/export.ts index e224f30a1bb0..16547970369c 100644 --- a/src/core/server/saved_objects/routes/export.ts +++ b/src/core/server/saved_objects/routes/export.ts @@ -165,7 +165,7 @@ export const registerExportRoute = ( }, catchAndReturnBoomErrors(async (context, req, res) => { const cleaned = cleanOptions(req.body); - const { typeRegistry, getExporter, getClient } = context.core.savedObjects; + const { typeRegistry, getExporter, getClient } = (await context.core).savedObjects; const supportedTypes = typeRegistry.getImportableAndExportableTypes().map((t) => t.name); let options: EitherExportOptions; diff --git a/src/core/server/saved_objects/routes/find.ts b/src/core/server/saved_objects/routes/find.ts index 6e009f80bda7..01ac9ae9025f 100644 --- a/src/core/server/saved_objects/routes/find.ts +++ b/src/core/server/saved_objects/routes/find.ts @@ -73,8 +73,8 @@ export const registerFindRoute = (router: IRouter, { coreUsageData }: RouteDepen }); } } - - const result = await context.core.savedObjects.client.find({ + const { savedObjects } = await context.core; + const result = await savedObjects.client.find({ perPage: query.per_page, page: query.page, type: Array.isArray(query.type) ? query.type : [query.type], diff --git a/src/core/server/saved_objects/routes/get.ts b/src/core/server/saved_objects/routes/get.ts index ae0656599a1e..2ea9a13bbce6 100644 --- a/src/core/server/saved_objects/routes/get.ts +++ b/src/core/server/saved_objects/routes/get.ts @@ -32,8 +32,9 @@ export const registerGetRoute = (router: IRouter, { coreUsageData }: RouteDepend const usageStatsClient = coreUsageData.getClient(); usageStatsClient.incrementSavedObjectsGet({ request: req }).catch(() => {}); - const savedObject = await context.core.savedObjects.client.get(type, id); - return res.ok({ body: savedObject }); + const { savedObjects } = await context.core; + const object = await savedObjects.client.get(type, id); + return res.ok({ body: object }); }) ); }; diff --git a/src/core/server/saved_objects/routes/import.ts b/src/core/server/saved_objects/routes/import.ts index d373dd5e63bc..545d01b45474 100644 --- a/src/core/server/saved_objects/routes/import.ts +++ b/src/core/server/saved_objects/routes/import.ts @@ -63,7 +63,7 @@ export const registerImportRoute = ( }, catchAndReturnBoomErrors(async (context, req, res) => { const { overwrite, createNewCopies } = req.query; - const { getClient, getImporter, typeRegistry } = context.core.savedObjects; + const { getClient, getImporter, typeRegistry } = (await context.core).savedObjects; const usageStatsClient = coreUsageData.getClient(); usageStatsClient diff --git a/src/core/server/saved_objects/routes/legacy_import_export/export.ts b/src/core/server/saved_objects/routes/legacy_import_export/export.ts index fd10e60c4c80..7141d74b7190 100644 --- a/src/core/server/saved_objects/routes/legacy_import_export/export.ts +++ b/src/core/server/saved_objects/routes/legacy_import_export/export.ts @@ -38,7 +38,7 @@ export const registerLegacyExportRoute = ( ); const ids = Array.isArray(req.query.dashboard) ? req.query.dashboard : [req.query.dashboard]; - const { client } = ctx.core.savedObjects; + const { client } = (await ctx.core).savedObjects; const usageStatsClient = coreUsageData.getClient(); usageStatsClient.incrementLegacyDashboardsExport({ request: req }).catch(() => {}); diff --git a/src/core/server/saved_objects/routes/legacy_import_export/import.ts b/src/core/server/saved_objects/routes/legacy_import_export/import.ts index 09027af81014..d98c14f9b620 100644 --- a/src/core/server/saved_objects/routes/legacy_import_export/import.ts +++ b/src/core/server/saved_objects/routes/legacy_import_export/import.ts @@ -46,7 +46,7 @@ export const registerLegacyImportRoute = ( "The import dashboard API '/api/kibana/dashboards/import' is deprecated. Use the saved objects import objects API '/api/saved_objects/_import' instead." ); - const { client } = ctx.core.savedObjects; + const { client } = (await ctx.core).savedObjects; const objects = req.body.objects as SavedObject[]; const { force, exclude } = req.query; diff --git a/src/core/server/saved_objects/routes/resolve.ts b/src/core/server/saved_objects/routes/resolve.ts index 78e85d17fe1f..ae09f6526baa 100644 --- a/src/core/server/saved_objects/routes/resolve.ts +++ b/src/core/server/saved_objects/routes/resolve.ts @@ -27,11 +27,12 @@ export const registerResolveRoute = (router: IRouter, { coreUsageData }: RouteDe }, router.handleLegacyErrors(async (context, req, res) => { const { type, id } = req.params; + const { savedObjects } = await context.core; const usageStatsClient = coreUsageData.getClient(); usageStatsClient.incrementSavedObjectsResolve({ request: req }).catch(() => {}); - const result = await context.core.savedObjects.client.resolve(type, id); + const result = await savedObjects.client.resolve(type, id); return res.ok({ body: result }); }) ); diff --git a/src/core/server/saved_objects/routes/resolve_import_errors.ts b/src/core/server/saved_objects/routes/resolve_import_errors.ts index f1fe2e9cfe43..bf536e906d7d 100644 --- a/src/core/server/saved_objects/routes/resolve_import_errors.ts +++ b/src/core/server/saved_objects/routes/resolve_import_errors.ts @@ -92,7 +92,7 @@ export const registerResolveImportErrorsRoute = ( }); } - const { getClient, getImporter, typeRegistry } = context.core.savedObjects; + const { getClient, getImporter, typeRegistry } = (await context.core).savedObjects; const includedHiddenTypes = chain(req.body.retries) .map('type') diff --git a/src/core/server/saved_objects/routes/update.ts b/src/core/server/saved_objects/routes/update.ts index f21fc183cdad..5383ab76a1e4 100644 --- a/src/core/server/saved_objects/routes/update.ts +++ b/src/core/server/saved_objects/routes/update.ts @@ -49,7 +49,8 @@ export const registerUpdateRoute = (router: IRouter, { coreUsageData }: RouteDep const usageStatsClient = coreUsageData.getClient(); usageStatsClient.incrementSavedObjectsUpdate({ request: req }).catch(() => {}); - const result = await context.core.savedObjects.client.update(type, id, attributes, options); + const { savedObjects } = await context.core; + const result = await savedObjects.client.update(type, id, attributes, options); return res.ok({ body: result }); }) ); diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md index 67aca94de642..2228c8fee879 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -9,6 +9,7 @@ import { AddConfigDeprecation } from '@kbn/config'; import { AnalyticsClient } from '@kbn/analytics-client'; import apm from 'elastic-apm-node'; +import { AwaitedProperties } from '@kbn/utility-types'; import Boom from '@hapi/boom'; import { ByteSizeValue } from '@kbn/config-schema'; import { CliArgs } from '@kbn/config'; @@ -446,6 +447,30 @@ export interface CorePreboot { preboot: PrebootServicePreboot; } +// @public +export interface CoreRequestHandlerContext { + // (undocumented) + deprecations: { + client: DeprecationsClient; + }; + // (undocumented) + elasticsearch: { + client: IScopedClusterClient; + }; + // (undocumented) + savedObjects: { + client: SavedObjectsClientContract; + typeRegistry: ISavedObjectTypeRegistry; + getClient: (options?: SavedObjectsClientProviderOptions) => SavedObjectsClientContract; + getExporter: (client: SavedObjectsClientContract) => ISavedObjectsExporter; + getImporter: (client: SavedObjectsClientContract) => ISavedObjectsImporter; + }; + // (undocumented) + uiSettings: { + client: IUiSettingsClient; + }; +} + // @internal export interface CoreServicesUsageData { // (undocumented) @@ -840,6 +865,11 @@ export interface CustomHttpResponseOptions = RequestHandlerContext & { + [Key in keyof T]: T[Key] extends Promise ? T[Key] : Promise; +}; + // @internal (undocumented) export const DEFAULT_APP_CATEGORIES: Record; @@ -1189,7 +1219,7 @@ export interface HttpServiceSetup { registerOnPreAuth: (handler: OnPreAuthHandler) => void; registerOnPreResponse: (handler: OnPreResponseHandler) => void; registerOnPreRouting: (handler: OnPreRoutingHandler) => void; - registerRouteHandlerContext: (contextName: ContextName, provider: RequestHandlerContextProvider) => RequestHandlerContextContainer; + registerRouteHandlerContext: >(contextName: ContextName, provider: RequestHandlerContextProvider) => RequestHandlerContextContainer; } // @public (undocumented) @@ -1221,7 +1251,7 @@ export interface IContextContainer { } // @public -export type IContextProvider = (context: Omit, ...rest: HandlerParameters) => Promise | Context[ContextName]; +export type IContextProvider = (context: Omit, ...rest: HandlerParameters) => MaybePromise>; // @public export interface ICspConfig { @@ -1840,26 +1870,14 @@ export interface RegisterDeprecationsConfig { export type RequestHandler

= (context: Context, request: KibanaRequest, response: ResponseFactory) => IKibanaResponse | Promise>; // @public -export interface RequestHandlerContext { +export interface RequestHandlerContext extends RequestHandlerContextBase { // (undocumented) - core: { - savedObjects: { - client: SavedObjectsClientContract; - typeRegistry: ISavedObjectTypeRegistry; - getClient: (options?: SavedObjectsClientProviderOptions) => SavedObjectsClientContract; - getExporter: (client: SavedObjectsClientContract) => ISavedObjectsExporter; - getImporter: (client: SavedObjectsClientContract) => ISavedObjectsImporter; - }; - elasticsearch: { - client: IScopedClusterClient; - }; - uiSettings: { - client: IUiSettingsClient; - }; - deprecations: { - client: DeprecationsClient; - }; - }; + core: Promise; +} + +// @public (undocumented) +export interface RequestHandlerContextBase { + resolve: >(parts: T[]) => Promise>>; } // @public diff --git a/src/core/server/server.ts b/src/core/server/server.ts index fc215d905581..c73e98f4bb6c 100644 --- a/src/core/server/server.ts +++ b/src/core/server/server.ts @@ -42,7 +42,6 @@ import { config as uiSettingsConfig } from './ui_settings'; import { config as statusConfig } from './status'; import { config as i18nConfig } from './i18n'; import { ContextService } from './context'; -import { RequestHandlerContext } from '.'; import { InternalCorePreboot, InternalCoreSetup, @@ -372,13 +371,9 @@ export class Server { } private registerCoreContext(coreSetup: InternalCoreSetup) { - coreSetup.http.registerRouteHandlerContext( - coreId, - 'core', - (context, req, res): RequestHandlerContext['core'] => { - return new CoreRouteHandlerContext(this.coreStart!, req); - } - ); + coreSetup.http.registerRouteHandlerContext(coreId, 'core', async (context, req, res) => { + return new CoreRouteHandlerContext(this.coreStart!, req); + }); } public setupCoreConfig() { diff --git a/src/core/server/ui_settings/routes/delete.ts b/src/core/server/ui_settings/routes/delete.ts index f8ab4d5d0c2c..87c6edf38642 100644 --- a/src/core/server/ui_settings/routes/delete.ts +++ b/src/core/server/ui_settings/routes/delete.ts @@ -23,7 +23,7 @@ export function registerDeleteRoute(router: IRouter) { { path: '/api/kibana/settings/{key}', validate }, async (context, request, response) => { try { - const uiSettingsClient = context.core.uiSettings.client; + const uiSettingsClient = (await context.core).uiSettings.client; await uiSettingsClient.remove(request.params.key); diff --git a/src/core/server/ui_settings/routes/get.ts b/src/core/server/ui_settings/routes/get.ts index 051d562c3980..0929330cf023 100644 --- a/src/core/server/ui_settings/routes/get.ts +++ b/src/core/server/ui_settings/routes/get.ts @@ -14,7 +14,7 @@ export function registerGetRoute(router: IRouter) { { path: '/api/kibana/settings', validate: false }, async (context, request, response) => { try { - const uiSettingsClient = context.core.uiSettings.client; + const uiSettingsClient = (await context.core).uiSettings.client; return response.ok({ body: { settings: await uiSettingsClient.getUserProvided(), diff --git a/src/core/server/ui_settings/routes/set.ts b/src/core/server/ui_settings/routes/set.ts index 7de287f4ebe6..91518fb6f347 100644 --- a/src/core/server/ui_settings/routes/set.ts +++ b/src/core/server/ui_settings/routes/set.ts @@ -26,7 +26,7 @@ export function registerSetRoute(router: IRouter) { { path: '/api/kibana/settings/{key}', validate }, async (context, request, response) => { try { - const uiSettingsClient = context.core.uiSettings.client; + const uiSettingsClient = (await context.core).uiSettings.client; const { key } = request.params; const { value } = request.body; diff --git a/src/core/server/ui_settings/routes/set_many.ts b/src/core/server/ui_settings/routes/set_many.ts index c4053dd3e7ee..f4f3f509bf92 100644 --- a/src/core/server/ui_settings/routes/set_many.ts +++ b/src/core/server/ui_settings/routes/set_many.ts @@ -21,7 +21,7 @@ const validate = { export function registerSetManyRoute(router: IRouter) { router.post({ path: '/api/kibana/settings', validate }, async (context, request, response) => { try { - const uiSettingsClient = context.core.uiSettings.client; + const uiSettingsClient = (await context.core).uiSettings.client; const { changes } = request.body; diff --git a/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker b/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker index fbf6d64170ee..ca81383ba639 100755 --- a/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker +++ b/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker @@ -40,6 +40,11 @@ kibana_vars=( csp.report_to data.autocomplete.valueSuggestions.terminateAfter data.autocomplete.valueSuggestions.timeout + unifiedSearch.autocomplete.valueSuggestions.terminateAfter + unifiedSearch.autocomplete.valueSuggestions.timeout + unifiedSearch.autocomplete.querySuggestions.enabled + unifiedSearch.autocomplete.valueSuggestions.enabled + unifiedSearch.autocomplete.valueSuggestions.tiers elasticsearch.customHeaders elasticsearch.hosts elasticsearch.logQueries diff --git a/src/plugins/console/common/constants/api.ts b/src/plugins/console/common/constants/api.ts index aa0fad1fe442..3c5aa1151935 100644 --- a/src/plugins/console/common/constants/api.ts +++ b/src/plugins/console/common/constants/api.ts @@ -7,3 +7,4 @@ */ export const API_BASE_PATH = '/api/console'; +export const KIBANA_API_PREFIX = 'kbn:'; diff --git a/src/plugins/console/common/constants/index.ts b/src/plugins/console/common/constants/index.ts index d8768af8fc8d..756a79883cbd 100644 --- a/src/plugins/console/common/constants/index.ts +++ b/src/plugins/console/common/constants/index.ts @@ -7,4 +7,4 @@ */ export { MAJOR_VERSION } from './plugin'; -export { API_BASE_PATH } from './api'; +export { API_BASE_PATH, KIBANA_API_PREFIX } from './api'; diff --git a/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.test.mock.tsx b/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.test.mock.tsx index dfed86a64362..b410e240151d 100644 --- a/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.test.mock.tsx +++ b/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.test.mock.tsx @@ -39,8 +39,8 @@ jest.mock('../../../../models/sense_editor', () => { }; }); -jest.mock('../../../../hooks/use_send_current_request_to_es/send_request_to_es', () => ({ - sendRequestToES: jest.fn(), +jest.mock('../../../../hooks/use_send_current_request/send_request', () => ({ + sendRequest: jest.fn(), })); jest.mock('../../../../../lib/autocomplete/get_endpoint_from_position', () => ({ getEndpointFromPosition: jest.fn(), diff --git a/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.test.tsx b/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.test.tsx index b942a6d83021..ba5f1e78d5f0 100644 --- a/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.test.tsx +++ b/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.test.tsx @@ -25,7 +25,7 @@ import { } from '../../../../contexts'; // Mocked functions -import { sendRequestToES } from '../../../../hooks/use_send_current_request_to_es/send_request_to_es'; +import { sendRequest } from '../../../../hooks/use_send_current_request/send_request'; import { getEndpointFromPosition } from '../../../../../lib/autocomplete/get_endpoint_from_position'; import type { DevToolsSettings } from '../../../../../services'; import * as consoleMenuActions from '../console_menu_actions'; @@ -58,15 +58,15 @@ describe('Legacy (Ace) Console Editor Component Smoke Test', () => { sandbox.restore(); }); - it('calls send current request to ES', async () => { + it('calls send current request', async () => { (getEndpointFromPosition as jest.Mock).mockReturnValue({ patterns: [] }); - (sendRequestToES as jest.Mock).mockRejectedValue({}); + (sendRequest as jest.Mock).mockRejectedValue({}); const editor = doMount(); act(() => { editor.find('button[data-test-subj~="sendRequestButton"]').simulate('click'); }); await nextTick(); - expect(sendRequestToES).toBeCalledTimes(1); + expect(sendRequest).toBeCalledTimes(1); }); it('opens docs', () => { diff --git a/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx b/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx index bafe9ee6ca15..d01a40bdd44b 100644 --- a/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx +++ b/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx @@ -26,7 +26,7 @@ import { ConsoleMenu } from '../../../../components'; import { useEditorReadContext, useServicesContext } from '../../../../contexts'; import { useSaveCurrentTextObject, - useSendCurrentRequestToES, + useSendCurrentRequest, useSetInputEditor, } from '../../../../hooks'; import * as senseEditor from '../../../../models/sense_editor'; @@ -72,7 +72,7 @@ function EditorUI({ initialTextValue, setEditorInstance }: EditorProps) { const { settings } = useEditorReadContext(); const setInputEditor = useSetInputEditor(); - const sendCurrentRequestToES = useSendCurrentRequestToES(); + const sendCurrentRequest = useSendCurrentRequest(); const saveCurrentTextObject = useSaveCurrentTextObject(); const editorRef = useRef(null); @@ -231,11 +231,11 @@ function EditorUI({ initialTextValue, setEditorInstance }: EditorProps) { if (!isKeyboardShortcutsDisabled) { registerCommands({ senseEditor: editorInstanceRef.current!, - sendCurrentRequestToES, + sendCurrentRequest, openDocumentation, }); } - }, [sendCurrentRequestToES, openDocumentation, settings]); + }, [openDocumentation, settings, sendCurrentRequest]); useEffect(() => { const { current: editor } = editorInstanceRef; @@ -262,7 +262,7 @@ function EditorUI({ initialTextValue, setEditorInstance }: EditorProps) { > void; + sendCurrentRequest: () => void; openDocumentation: () => void; } @@ -24,11 +24,7 @@ const COMMANDS = { GO_TO_LINE: 'gotoline', }; -export function registerCommands({ - senseEditor, - sendCurrentRequestToES, - openDocumentation, -}: Actions) { +export function registerCommands({ senseEditor, sendCurrentRequest, openDocumentation }: Actions) { const throttledAutoIndent = throttle(() => senseEditor.autoIndent(), 500, { leading: true, trailing: true, @@ -39,7 +35,7 @@ export function registerCommands({ keys: { win: 'Ctrl-Enter', mac: 'Command-Enter' }, name: COMMANDS.SEND_TO_ELASTICSEARCH, fn: () => { - sendCurrentRequestToES(); + sendCurrentRequest(); }, }); diff --git a/src/plugins/console/public/application/hooks/index.ts b/src/plugins/console/public/application/hooks/index.ts index 1a9b4e5c472b..1996330bef66 100644 --- a/src/plugins/console/public/application/hooks/index.ts +++ b/src/plugins/console/public/application/hooks/index.ts @@ -8,6 +8,6 @@ export { useSetInputEditor } from './use_set_input_editor'; export { useRestoreRequestFromHistory } from './use_restore_request_from_history'; -export { useSendCurrentRequestToES } from './use_send_current_request_to_es'; +export { useSendCurrentRequest } from './use_send_current_request'; export { useSaveCurrentTextObject } from './use_save_current_text_object'; export { useDataInit } from './use_data_init'; diff --git a/src/plugins/console/public/application/hooks/use_send_current_request_to_es/index.ts b/src/plugins/console/public/application/hooks/use_send_current_request/index.ts similarity index 81% rename from src/plugins/console/public/application/hooks/use_send_current_request_to_es/index.ts rename to src/plugins/console/public/application/hooks/use_send_current_request/index.ts index df2431f1f6f4..33bdbef87f2e 100644 --- a/src/plugins/console/public/application/hooks/use_send_current_request_to_es/index.ts +++ b/src/plugins/console/public/application/hooks/use_send_current_request/index.ts @@ -6,4 +6,4 @@ * Side Public License, v 1. */ -export { useSendCurrentRequestToES } from './use_send_current_request_to_es'; +export { useSendCurrentRequest } from './use_send_current_request'; diff --git a/src/plugins/console/public/application/hooks/use_send_current_request_to_es/send_request_to_es.test.ts b/src/plugins/console/public/application/hooks/use_send_current_request/send_request.test.ts similarity index 66% rename from src/plugins/console/public/application/hooks/use_send_current_request_to_es/send_request_to_es.test.ts rename to src/plugins/console/public/application/hooks/use_send_current_request/send_request.test.ts index 8578e271f37b..60ced085c689 100644 --- a/src/plugins/console/public/application/hooks/use_send_current_request_to_es/send_request_to_es.test.ts +++ b/src/plugins/console/public/application/hooks/use_send_current_request/send_request.test.ts @@ -8,14 +8,14 @@ import type { ContextValue } from '../../contexts'; -jest.mock('./send_request_to_es', () => ({ sendRequestToES: jest.fn(() => Promise.resolve()) })); +jest.mock('./send_request', () => ({ sendRequest: jest.fn(() => Promise.resolve()) })); -import { sendRequestToES } from './send_request_to_es'; +import { sendRequest } from './send_request'; import { serviceContextMock } from '../../contexts/services_context.mock'; -const mockedSendRequestToES = sendRequestToES as jest.Mock; +const mockedSendRequest = sendRequest as jest.Mock; -describe('sendRequestToES', () => { +describe('sendRequest', () => { let mockContextValue: ContextValue; beforeEach(() => { @@ -26,8 +26,8 @@ describe('sendRequestToES', () => { jest.resetAllMocks(); }); - it('should send request to ES', async () => { - mockedSendRequestToES.mockResolvedValue([ + it('should send request', async () => { + mockedSendRequest.mockResolvedValue([ { response: { statusCode: 200, @@ -40,17 +40,17 @@ describe('sendRequestToES', () => { http: mockContextValue.services.http, requests: [{ method: 'PUT', url: 'test', data: [] }], }; - const results = await sendRequestToES(args); + const results = await sendRequest(args); const [request] = results; expect(request.response.statusCode).toEqual(200); expect(request.response.value).toContain('"acknowledged": true'); - expect(mockedSendRequestToES).toHaveBeenCalledWith(args); - expect(mockedSendRequestToES).toHaveBeenCalledTimes(1); + expect(mockedSendRequest).toHaveBeenCalledWith(args); + expect(mockedSendRequest).toHaveBeenCalledTimes(1); }); - it('should send multiple requests to ES', async () => { - mockedSendRequestToES.mockResolvedValue([ + it('should send multiple requests', async () => { + mockedSendRequest.mockResolvedValue([ { response: { statusCode: 200, @@ -70,17 +70,17 @@ describe('sendRequestToES', () => { { method: 'GET', url: 'test-2', data: [] }, ], }; - const results = await sendRequestToES(args); + const results = await sendRequest(args); const [firstRequest, secondRequest] = results; expect(firstRequest.response.statusCode).toEqual(200); expect(secondRequest.response.statusCode).toEqual(200); - expect(mockedSendRequestToES).toHaveBeenCalledWith(args); - expect(mockedSendRequestToES).toHaveBeenCalledTimes(1); + expect(mockedSendRequest).toHaveBeenCalledWith(args); + expect(mockedSendRequest).toHaveBeenCalledTimes(1); }); it('should handle errors', async () => { - mockedSendRequestToES.mockRejectedValue({ + mockedSendRequest.mockRejectedValue({ response: { statusCode: 500, statusText: 'error', @@ -88,45 +88,46 @@ describe('sendRequestToES', () => { }); try { - await sendRequestToES({ + await sendRequest({ http: mockContextValue.services.http, requests: [{ method: 'GET', url: 'test', data: [] }], }); } catch (error) { expect(error.response.statusCode).toEqual(500); expect(error.response.statusText).toEqual('error'); - expect(mockedSendRequestToES).toHaveBeenCalledTimes(1); + expect(mockedSendRequest).toHaveBeenCalledTimes(1); } }); + describe('successful response value', () => { describe('with text', () => { it('should return value with lines separated', async () => { - mockedSendRequestToES.mockResolvedValue('\ntest_index-1 [] \ntest_index-2 []\n'); - const response = await sendRequestToES({ + mockedSendRequest.mockResolvedValue('\ntest_index-1 []\ntest_index-2 []\n'); + const response = await sendRequest({ http: mockContextValue.services.http, requests: [{ method: 'GET', url: 'test-1', data: [] }], }); expect(response).toMatchInlineSnapshot(` " - test_index-1 [] + test_index-1 [] test_index-2 [] " `); - expect(mockedSendRequestToES).toHaveBeenCalledTimes(1); + expect(mockedSendRequest).toHaveBeenCalledTimes(1); }); }); describe('with parsed json', () => { it('should stringify value', async () => { - mockedSendRequestToES.mockResolvedValue(JSON.stringify({ test: 'some value' })); - const response = await sendRequestToES({ + mockedSendRequest.mockResolvedValue(JSON.stringify({ test: 'some value' })); + const response = await sendRequest({ http: mockContextValue.services.http, requests: [{ method: 'GET', url: 'test-2', data: [] }], }); expect(typeof response).toBe('string'); - expect(mockedSendRequestToES).toHaveBeenCalledTimes(1); + expect(mockedSendRequest).toHaveBeenCalledTimes(1); }); }); }); diff --git a/src/plugins/console/public/application/hooks/use_send_current_request_to_es/send_request_to_es.ts b/src/plugins/console/public/application/hooks/use_send_current_request/send_request.ts similarity index 69% rename from src/plugins/console/public/application/hooks/use_send_current_request_to_es/send_request_to_es.ts rename to src/plugins/console/public/application/hooks/use_send_current_request/send_request.ts index 451198aaf2d2..1247f3f78aa6 100644 --- a/src/plugins/console/public/application/hooks/use_send_current_request_to_es/send_request_to_es.ts +++ b/src/plugins/console/public/application/hooks/use_send_current_request/send_request.ts @@ -15,12 +15,12 @@ import { BaseResponseType } from '../../../types'; const { collapseLiteralStrings } = XJson; -export interface EsRequestArgs { +export interface RequestArgs { http: HttpSetup; requests: Array<{ url: string; method: string; data: string[] }>; } -export interface ESResponseObject { +export interface ResponseObject { statusCode: number; statusText: string; timeMs: number; @@ -28,17 +28,17 @@ export interface ESResponseObject { value: V; } -export interface ESRequestResult { +export interface RequestResult { request: { data: string; method: string; path: string }; - response: ESResponseObject; + response: ResponseObject; } let CURRENT_REQ_ID = 0; -export function sendRequestToES(args: EsRequestArgs): Promise { +export function sendRequest(args: RequestArgs): Promise { const requests = args.requests.slice(); return new Promise((resolve, reject) => { const reqId = ++CURRENT_REQ_ID; - const results: ESRequestResult[] = []; + const results: RequestResult[] = []; if (reqId !== CURRENT_REQ_ID) { return; } @@ -59,11 +59,11 @@ export function sendRequestToES(args: EsRequestArgs): Promise return; } const req = requests.shift()!; - const esPath = req.url; - const esMethod = req.method; - let esData = collapseLiteralStrings(req.data.join('\n')); - if (esData) { - esData += '\n'; + const path = req.url; + const method = req.method; + let data = collapseLiteralStrings(req.data.join('\n')); + if (data) { + data += '\n'; } // append a new line for bulk requests. const startTime = Date.now(); @@ -71,9 +71,9 @@ export function sendRequestToES(args: EsRequestArgs): Promise try { const { response, body } = await es.send({ http: args.http, - method: esMethod, - path: esPath, - data: esData, + method, + path, + data, asResponse: true, }); @@ -115,9 +115,9 @@ export function sendRequestToES(args: EsRequestArgs): Promise value, }, request: { - data: esData, - method: esMethod, - path: esPath, + data, + method, + path, }, }); @@ -127,25 +127,19 @@ export function sendRequestToES(args: EsRequestArgs): Promise } } catch (error) { let value; - let contentType: string | null = ''; + const { response, body } = error as IHttpFetchError; + const contentType = response?.headers.get('Content-Type') ?? ''; + const statusCode = response?.status ?? 500; + const statusText = error?.response?.statusText ?? 'error'; - const { response, body = {} } = error as IHttpFetchError; - if (response) { - const { status, headers } = response; - if (body) { - value = JSON.stringify(body, null, 2); // ES error should be shown - contentType = headers.get('Content-Type'); - } else { - value = 'Request failed to get to the server (status code: ' + status + ')'; - contentType = headers.get('Content-Type'); - } - - if (isMultiRequest) { - value = '# ' + req.method + ' ' + req.url + '\n' + value; - } + if (body) { + value = JSON.stringify(body, null, 2); } else { - value = - "\n\nFailed to connect to Console's backend.\nPlease check the Kibana server is up and running"; + value = 'Request failed to get to the server (status code: ' + statusCode + ')'; + } + + if (isMultiRequest) { + value = '# ' + req.method + ' ' + req.url + '\n' + value; } reject({ @@ -153,13 +147,13 @@ export function sendRequestToES(args: EsRequestArgs): Promise value, contentType, timeMs: Date.now() - startTime, - statusCode: error?.response?.status ?? 500, - statusText: error?.response?.statusText ?? 'error', + statusCode, + statusText, }, request: { - data: esData, - method: esMethod, - path: esPath, + data, + method, + path, }, }); } diff --git a/src/plugins/console/public/application/hooks/use_send_current_request_to_es/track.ts b/src/plugins/console/public/application/hooks/use_send_current_request/track.ts similarity index 100% rename from src/plugins/console/public/application/hooks/use_send_current_request_to_es/track.ts rename to src/plugins/console/public/application/hooks/use_send_current_request/track.ts diff --git a/src/plugins/console/public/application/hooks/use_send_current_request_to_es/use_send_current_request_to_es.test.tsx b/src/plugins/console/public/application/hooks/use_send_current_request/use_send_current_request.test.tsx similarity index 80% rename from src/plugins/console/public/application/hooks/use_send_current_request_to_es/use_send_current_request_to_es.test.tsx rename to src/plugins/console/public/application/hooks/use_send_current_request/use_send_current_request.test.tsx index e0131dc116a3..d16dc3f832d3 100644 --- a/src/plugins/console/public/application/hooks/use_send_current_request_to_es/use_send_current_request_to_es.test.tsx +++ b/src/plugins/console/public/application/hooks/use_send_current_request/use_send_current_request.test.tsx @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -jest.mock('./send_request_to_es', () => ({ sendRequestToES: jest.fn() })); +jest.mock('./send_request', () => ({ sendRequest: jest.fn() })); jest.mock('../../contexts/editor_context/editor_registry', () => ({ instance: { getInputEditor: jest.fn() }, })); @@ -21,10 +21,10 @@ import { serviceContextMock } from '../../contexts/services_context.mock'; import { useRequestActionContext } from '../../contexts/request_context'; import { instance as editorRegistry } from '../../contexts/editor_context/editor_registry'; -import { sendRequestToES } from './send_request_to_es'; -import { useSendCurrentRequestToES } from './use_send_current_request_to_es'; +import { sendRequest } from './send_request'; +import { useSendCurrentRequest } from './use_send_current_request'; -describe('useSendCurrentRequestToES', () => { +describe('useSendCurrentRequest', () => { let mockContextValue: ContextValue; let dispatch: (...args: unknown[]) => void; const contexts = ({ children }: { children: JSX.Element }) => ( @@ -41,18 +41,18 @@ describe('useSendCurrentRequestToES', () => { jest.resetAllMocks(); }); - it('calls send request to ES', async () => { + it('calls send request', async () => { // Set up mocks (mockContextValue.services.settings.toJSON as jest.Mock).mockReturnValue({}); // This request should succeed - (sendRequestToES as jest.Mock).mockResolvedValue([]); + (sendRequest as jest.Mock).mockResolvedValue([]); (editorRegistry.getInputEditor as jest.Mock).mockImplementation(() => ({ getRequestsInRange: () => ['test'], })); - const { result } = renderHook(() => useSendCurrentRequestToES(), { wrapper: contexts }); + const { result } = renderHook(() => useSendCurrentRequest(), { wrapper: contexts }); await act(() => result.current()); - expect(sendRequestToES).toHaveBeenCalledWith({ + expect(sendRequest).toHaveBeenCalledWith({ http: mockContextValue.services.http, requests: ['test'], }); @@ -64,12 +64,12 @@ describe('useSendCurrentRequestToES', () => { it('handles known errors', async () => { // Set up mocks - (sendRequestToES as jest.Mock).mockRejectedValue({ response: 'nada' }); + (sendRequest as jest.Mock).mockRejectedValue({ response: 'nada' }); (editorRegistry.getInputEditor as jest.Mock).mockImplementation(() => ({ getRequestsInRange: () => ['test'], })); - const { result } = renderHook(() => useSendCurrentRequestToES(), { wrapper: contexts }); + const { result } = renderHook(() => useSendCurrentRequest(), { wrapper: contexts }); await act(() => result.current()); // Second call should be the request failure const [, [requestFailedCall]] = (dispatch as jest.Mock).mock.calls; @@ -80,12 +80,12 @@ describe('useSendCurrentRequestToES', () => { it('handles unknown errors', async () => { // Set up mocks - (sendRequestToES as jest.Mock).mockRejectedValue(NaN /* unexpected error value */); + (sendRequest as jest.Mock).mockRejectedValue(NaN /* unexpected error value */); (editorRegistry.getInputEditor as jest.Mock).mockImplementation(() => ({ getRequestsInRange: () => ['test'], })); - const { result } = renderHook(() => useSendCurrentRequestToES(), { wrapper: contexts }); + const { result } = renderHook(() => useSendCurrentRequest(), { wrapper: contexts }); await act(() => result.current()); // Second call should be the request failure const [, [requestFailedCall]] = (dispatch as jest.Mock).mock.calls; @@ -100,7 +100,7 @@ describe('useSendCurrentRequestToES', () => { it('notifies the user about save to history errors once only', async () => { // Set up mocks - (sendRequestToES as jest.Mock).mockReturnValue( + (sendRequest as jest.Mock).mockReturnValue( [{ request: {} }, { request: {} }] /* two responses to save history */ ); (mockContextValue.services.settings.toJSON as jest.Mock).mockReturnValue({}); @@ -112,7 +112,7 @@ describe('useSendCurrentRequestToES', () => { getRequestsInRange: () => ['test', 'test'], })); - const { result } = renderHook(() => useSendCurrentRequestToES(), { wrapper: contexts }); + const { result } = renderHook(() => useSendCurrentRequest(), { wrapper: contexts }); await act(() => result.current()); expect(dispatch).toHaveBeenCalledTimes(2); diff --git a/src/plugins/console/public/application/hooks/use_send_current_request_to_es/use_send_current_request_to_es.ts b/src/plugins/console/public/application/hooks/use_send_current_request/use_send_current_request.ts similarity index 96% rename from src/plugins/console/public/application/hooks/use_send_current_request_to_es/use_send_current_request_to_es.ts rename to src/plugins/console/public/application/hooks/use_send_current_request/use_send_current_request.ts index e7c436c9806b..ed08304d8d66 100644 --- a/src/plugins/console/public/application/hooks/use_send_current_request_to_es/use_send_current_request_to_es.ts +++ b/src/plugins/console/public/application/hooks/use_send_current_request/use_send_current_request.ts @@ -16,10 +16,10 @@ import { retrieveAutoCompleteInfo } from '../../../lib/mappings/mappings'; import { instance as registry } from '../../contexts/editor_context/editor_registry'; import { useRequestActionContext, useServicesContext } from '../../contexts'; import { StorageQuotaError } from '../../components/storage_quota_error'; -import { sendRequestToES } from './send_request_to_es'; +import { sendRequest } from './send_request'; import { track } from './track'; -export const useSendCurrentRequestToES = () => { +export const useSendCurrentRequest = () => { const { services: { history, settings, notifications, trackUiMetric, http }, theme$, @@ -46,7 +46,7 @@ export const useSendCurrentRequestToES = () => { // Fire and forget setTimeout(() => track(requests, editor, trackUiMetric), 0); - const results = await sendRequestToES({ http, requests }); + const results = await sendRequest({ http, requests }); let saveToHistoryError: undefined | Error; const { isHistoryDisabled } = settings.toJSON(); diff --git a/src/plugins/console/public/application/models/sense_editor/sense_editor.test.js b/src/plugins/console/public/application/models/sense_editor/sense_editor.test.js index 0889b98c6938..ff9d245f6127 100644 --- a/src/plugins/console/public/application/models/sense_editor/sense_editor.test.js +++ b/src/plugins/console/public/application/models/sense_editor/sense_editor.test.js @@ -455,11 +455,11 @@ describe('Editor', () => { editorInput1, { start: { lineNumber: 7 }, end: { lineNumber: 14 } }, ` -curl -XGET "http://localhost:9200/_stats?level=shards" +curl -XGET "http://localhost:9200/_stats?level=shards" -H "kbn-xsrf: reporting" #in between comment -curl -XPUT "http://localhost:9200/index_1/type1/1" -H 'Content-Type: application/json' -d' +curl -XPUT "http://localhost:9200/index_1/type1/1" -H "kbn-xsrf: reporting" -H "Content-Type: application/json" -d' { "f": 1 }'`.trim() @@ -470,7 +470,7 @@ curl -XPUT "http://localhost:9200/index_1/type1/1" -H 'Content-Type: application editorInput1, { start: { lineNumber: 29 }, end: { lineNumber: 33 } }, ` -curl -XPOST "http://localhost:9200/_sql?format=txt" -H 'Content-Type: application/json' -d' +curl -XPOST "http://localhost:9200/_sql?format=txt" -H "kbn-xsrf: reporting" -H "Content-Type: application/json" -d' { "query": "SELECT prenom FROM claude_index WHERE prenom = '\\''claude'\\'' ", "fetch_size": 1 diff --git a/src/plugins/console/public/application/models/sense_editor/sense_editor.ts b/src/plugins/console/public/application/models/sense_editor/sense_editor.ts index 5e8ca35f287b..ac65afce2c18 100644 --- a/src/plugins/console/public/application/models/sense_editor/sense_editor.ts +++ b/src/plugins/console/public/application/models/sense_editor/sense_editor.ts @@ -14,7 +14,7 @@ import RowParser from '../../../lib/row_parser'; import * as utils from '../../../lib/utils'; // @ts-ignore -import * as es from '../../../lib/es/es'; +import { constructUrl } from '../../../lib/es/es'; import { CoreEditor, Position, Range } from '../../../types'; import { createTokenIterator } from '../../factories'; @@ -467,21 +467,22 @@ export class SenseEditor { return req; } - const esPath = req.url; - const esMethod = req.method; - const esData = req.data; + const path = req.url; + const method = req.method; + const data = req.data; // this is the first url defined in elasticsearch.hosts - const url = es.constructESUrl(elasticsearchBaseUrl, esPath); + const url = constructUrl(elasticsearchBaseUrl, path); - let ret = 'curl -X' + esMethod + ' "' + url + '"'; - if (esData && esData.length) { - ret += " -H 'Content-Type: application/json' -d'\n"; - const dataAsString = collapseLiteralStrings(esData.join('\n')); + // Append 'kbn-xsrf' header to bypass (XSRF/CSRF) protections + let ret = `curl -X${method.toUpperCase()} "${url}" -H "kbn-xsrf: reporting"`; + if (data && data.length) { + ret += ` -H "Content-Type: application/json" -d'\n`; + const dataAsString = collapseLiteralStrings(data.join('\n')); // We escape single quoted strings that that are wrapped in single quoted strings ret += dataAsString.replace(/'/g, "'\\''"); - if (esData.length > 1) { + if (data.length > 1) { ret += '\n'; } // end with a new line ret += "'"; diff --git a/src/plugins/console/public/application/stores/request.ts b/src/plugins/console/public/application/stores/request.ts index 099ab24326d3..8056ab5a7987 100644 --- a/src/plugins/console/public/application/stores/request.ts +++ b/src/plugins/console/public/application/stores/request.ts @@ -10,18 +10,18 @@ import { Reducer } from 'react'; import { produce } from 'immer'; import { identity } from 'fp-ts/lib/function'; import { BaseResponseType } from '../../types/common'; -import { ESRequestResult } from '../hooks/use_send_current_request_to_es/send_request_to_es'; +import { RequestResult } from '../hooks/use_send_current_request/send_request'; export type Actions = | { type: 'sendRequest'; payload: undefined } - | { type: 'requestSuccess'; payload: { data: ESRequestResult[] } } - | { type: 'requestFail'; payload: ESRequestResult | undefined }; + | { type: 'requestSuccess'; payload: { data: RequestResult[] } } + | { type: 'requestFail'; payload: RequestResult | undefined }; export interface Store { requestInFlight: boolean; lastResult: { - data: ESRequestResult[] | null; - error?: ESRequestResult; + data: RequestResult[] | null; + error?: RequestResult; }; } diff --git a/src/plugins/console/public/lib/es/es.ts b/src/plugins/console/public/lib/es/es.ts index 2a4059d664e6..10d0ad95b049 100644 --- a/src/plugins/console/public/lib/es/es.ts +++ b/src/plugins/console/public/lib/es/es.ts @@ -6,8 +6,9 @@ * Side Public License, v 1. */ -import type { HttpFetchOptions, HttpResponse, HttpSetup } from '@kbn/core/public'; -import { API_BASE_PATH } from '../../../common/constants'; +import type { HttpResponse, HttpSetup } from '@kbn/core/public'; +import { trimStart } from 'lodash'; +import { API_BASE_PATH, KIBANA_API_PREFIX } from '../../../common/constants'; const esVersion: string[] = []; @@ -20,7 +21,7 @@ export function getContentType(body: unknown) { return 'application/json'; } -interface SendProps { +interface SendConfig { http: HttpSetup; method: string; path: string; @@ -30,6 +31,8 @@ interface SendProps { asResponse?: boolean; } +type Method = 'get' | 'post' | 'delete' | 'put' | 'patch' | 'head'; + export async function send({ http, method, @@ -38,18 +41,48 @@ export async function send({ asSystemRequest = false, withProductOrigin = false, asResponse = false, -}: SendProps) { - const options: HttpFetchOptions = { +}: SendConfig) { + const kibanaRequestUrl = getKibanaRequestUrl(path); + + if (kibanaRequestUrl) { + const httpMethod = method.toLowerCase() as Method; + const url = new URL(kibanaRequestUrl); + const { pathname, searchParams } = url; + const query = Object.fromEntries(searchParams.entries()); + const body = ['post', 'put', 'patch'].includes(httpMethod) ? data : null; + + return await http[httpMethod](pathname, { + body, + query, + asResponse, + asSystemRequest, + }); + } + + return await http.post(`${API_BASE_PATH}/proxy`, { query: { path, method, ...(withProductOrigin && { withProductOrigin }) }, body: data, asResponse, asSystemRequest, - }; + }); +} + +function getKibanaRequestUrl(path: string) { + const isKibanaApiRequest = path.startsWith(KIBANA_API_PREFIX); + const kibanaBasePath = window.location.origin; - return await http.post(`${API_BASE_PATH}/proxy`, options); + if (isKibanaApiRequest) { + // window.location.origin is used as a Kibana public base path for sending requests in cURL commands. E.g. "Copy as cURL". + return `${kibanaBasePath}/${trimStart(path.replace(KIBANA_API_PREFIX, ''), '/')}`; + } } -export function constructESUrl(baseUri: string, path: string) { +export function constructUrl(baseUri: string, path: string) { + const kibanaRequestUrl = getKibanaRequestUrl(path); + + if (kibanaRequestUrl) { + return kibanaRequestUrl; + } baseUri = baseUri.replace(/\/+$/, ''); path = path.replace(/^\/+/, ''); return baseUri + '/' + path; diff --git a/src/plugins/console/public/lib/es/index.ts b/src/plugins/console/public/lib/es/index.ts index 61d34ba96ec0..f83893e93713 100644 --- a/src/plugins/console/public/lib/es/index.ts +++ b/src/plugins/console/public/lib/es/index.ts @@ -6,4 +6,4 @@ * Side Public License, v 1. */ -export { send, constructESUrl, getContentType, getVersion } from './es'; +export { send, constructUrl, getContentType, getVersion } from './es'; diff --git a/src/plugins/controls/common/control_types/options_list/types.ts b/src/plugins/controls/common/control_types/options_list/types.ts index 9c051e4ca623..0f889bed7bac 100644 --- a/src/plugins/controls/common/control_types/options_list/types.ts +++ b/src/plugins/controls/common/control_types/options_list/types.ts @@ -8,14 +8,11 @@ import { BoolQuery } from '@kbn/es-query'; import { FieldSpec } from '@kbn/data-views-plugin/common'; -import { ControlInput } from '../../types'; +import { DataControlInput } from '../../types'; export const OPTIONS_LIST_CONTROL = 'optionsListControl'; -export interface OptionsListEmbeddableInput extends ControlInput { - fieldName: string; - dataViewId: string; - +export interface OptionsListEmbeddableInput extends DataControlInput { selectedOptions?: string[]; singleSelect?: boolean; loading?: boolean; diff --git a/src/plugins/controls/common/control_types/range_slider/types.ts b/src/plugins/controls/common/control_types/range_slider/types.ts index e63ec0337a57..a975fdd27ac3 100644 --- a/src/plugins/controls/common/control_types/range_slider/types.ts +++ b/src/plugins/controls/common/control_types/range_slider/types.ts @@ -6,14 +6,12 @@ * Side Public License, v 1. */ -import { ControlInput } from '../../types'; +import { DataControlInput } from '../../types'; export const RANGE_SLIDER_CONTROL = 'rangeSliderControl'; export type RangeValue = [string, string]; -export interface RangeSliderEmbeddableInput extends ControlInput { - fieldName: string; - dataViewId: string; +export interface RangeSliderEmbeddableInput extends DataControlInput { value: RangeValue; } diff --git a/src/plugins/controls/common/control_types/time_slider/types.ts b/src/plugins/controls/common/control_types/time_slider/types.ts index 73d364da80ca..31272380becd 100644 --- a/src/plugins/controls/common/control_types/time_slider/types.ts +++ b/src/plugins/controls/common/control_types/time_slider/types.ts @@ -6,12 +6,10 @@ * Side Public License, v 1. */ -import { ControlInput } from '../../types'; +import { DataControlInput } from '../../types'; export const TIME_SLIDER_CONTROL = 'timeSlider'; -export interface TimeSliderControlEmbeddableInput extends ControlInput { - fieldName: string; - dataViewId: string; +export interface TimeSliderControlEmbeddableInput extends DataControlInput { value?: [number | null, number | null]; } diff --git a/src/plugins/controls/common/types.ts b/src/plugins/controls/common/types.ts index 3715198d7cfe..abb24299e818 100644 --- a/src/plugins/controls/common/types.ts +++ b/src/plugins/controls/common/types.ts @@ -27,3 +27,8 @@ export type ControlInput = EmbeddableInput & { controlStyle?: ControlStyle; ignoreParentSettings?: ParentIgnoreSettings; }; + +export type DataControlInput = ControlInput & { + fieldName: string; + dataViewId: string; +}; diff --git a/src/plugins/controls/kibana.json b/src/plugins/controls/kibana.json index 20afd63505a7..e87af3f517af 100644 --- a/src/plugins/controls/kibana.json +++ b/src/plugins/controls/kibana.json @@ -17,7 +17,8 @@ "expressions", "embeddable", "dataViews", - "data" + "data", + "unifiedSearch" ], "optionalPlugins": [] } diff --git a/src/plugins/controls/public/__stories__/controls.stories.tsx b/src/plugins/controls/public/__stories__/controls.stories.tsx index 74d0d3e9de6a..481016af72a3 100644 --- a/src/plugins/controls/public/__stories__/controls.stories.tsx +++ b/src/plugins/controls/public/__stories__/controls.stories.tsx @@ -31,7 +31,7 @@ import { decorators } from './decorators'; import { ControlsPanels } from '../control_group/types'; import { ControlGroupContainer } from '../control_group'; import { pluginServices, registry } from '../services/storybook'; -import { replaceValueSuggestionMethod } from '../services/storybook/data'; +import { replaceValueSuggestionMethod } from '../services/storybook/unified_search'; import { injectStorybookDataView } from '../services/storybook/data_views'; import { populateStorybookControlFactories } from './storybook_control_factories'; import { OptionsListRequest } from '../services/options_list'; diff --git a/src/plugins/controls/public/control_types/range_slider/range_slider.component.tsx b/src/plugins/controls/public/control_types/range_slider/range_slider.component.tsx index f1002290eab3..259b6bd7f66a 100644 --- a/src/plugins/controls/public/control_types/range_slider/range_slider.component.tsx +++ b/src/plugins/controls/public/control_types/range_slider/range_slider.component.tsx @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import React, { FC, useCallback, useState } from 'react'; +import React, { FC, useCallback } from 'react'; import { BehaviorSubject } from 'rxjs'; import { DataViewField } from '@kbn/data-views-plugin/public'; @@ -45,16 +45,13 @@ export const RangeSliderComponent: FC = ({ componentStateSubject }) => { componentStateSubject.getValue() ); - const { value = ['', ''], id, title } = useEmbeddableSelector((state) => state); - - const [selectedValue, setSelectedValue] = useState(value || ['', '']); + const { value, id, title } = useEmbeddableSelector((state) => state); const onChangeComplete = useCallback( (range: RangeValue) => { dispatch(selectRange(range)); - setSelectedValue(range); }, - [selectRange, setSelectedValue, dispatch] + [selectRange, dispatch] ); return ( @@ -64,7 +61,7 @@ export const RangeSliderComponent: FC = ({ componentStateSubject }) => { min={min} max={max} title={title} - value={selectedValue} + value={value ?? ['', '']} onChange={onChangeComplete} fieldFormatter={fieldFormatter} /> diff --git a/src/plugins/controls/public/control_types/range_slider/range_slider_embeddable.tsx b/src/plugins/controls/public/control_types/range_slider/range_slider_embeddable.tsx index 2777f45d026b..1ad34fd361ac 100644 --- a/src/plugins/controls/public/control_types/range_slider/range_slider_embeddable.tsx +++ b/src/plugins/controls/public/control_types/range_slider/range_slider_embeddable.tsx @@ -311,7 +311,7 @@ export class RangeSliderEmbeddable extends Embeddable { + public reload = () => { this.fetchMinMax(); }; diff --git a/src/plugins/controls/public/control_types/range_slider/range_slider_embeddable_factory.tsx b/src/plugins/controls/public/control_types/range_slider/range_slider_embeddable_factory.tsx index 74bb2d23dbd8..bd8b8a394988 100644 --- a/src/plugins/controls/public/control_types/range_slider/range_slider_embeddable_factory.tsx +++ b/src/plugins/controls/public/control_types/range_slider/range_slider_embeddable_factory.tsx @@ -37,8 +37,8 @@ export class RangeSliderEmbeddableFactory ) => { if ( embeddable && - (!deepEqual(newInput.fieldName, embeddable.getInput().fieldName) || - !deepEqual(newInput.dataViewId, embeddable.getInput().dataViewId)) + ((newInput.fieldName && !deepEqual(newInput.fieldName, embeddable.getInput().fieldName)) || + (newInput.dataViewId && !deepEqual(newInput.dataViewId, embeddable.getInput().dataViewId))) ) { // if the field name or data view id has changed in this editing session, selected values are invalid, so reset them. newInput.value = ['', '']; diff --git a/src/plugins/controls/public/control_types/time_slider/time_slider.tsx b/src/plugins/controls/public/control_types/time_slider/time_slider.tsx index 61a47d8779a1..d25d4f145a99 100644 --- a/src/plugins/controls/public/control_types/time_slider/time_slider.tsx +++ b/src/plugins/controls/public/control_types/time_slider/time_slider.tsx @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import React, { FC, useCallback, useState, useMemo } from 'react'; +import React, { FC, useCallback, useMemo } from 'react'; import { BehaviorSubject } from 'rxjs'; import { debounce } from 'lodash'; import { useReduxEmbeddableContext } from '@kbn/presentation-util-plugin/public'; @@ -59,10 +59,6 @@ export const TimeSlider: FC = ({ const { value } = useEmbeddableSelector((state) => state); - const [selectedValue, setSelectedValue] = useState<[number | null, number | null]>( - value || [null, null] - ); - const dispatchChange = useCallback( (range: [number | null, number | null]) => { dispatch(selectRange(range)); @@ -75,15 +71,14 @@ export const TimeSlider: FC = ({ const onChangeComplete = useCallback( (range: [number | null, number | null]) => { debouncedDispatchChange(range); - setSelectedValue(range); }, - [setSelectedValue, debouncedDispatchChange] + [debouncedDispatchChange] ); return ( { if ( embeddable && - (!deepEqual(newInput.fieldName, embeddable.getInput().fieldName) || - !deepEqual(newInput.dataViewId, embeddable.getInput().dataViewId)) + ((newInput.fieldName && !deepEqual(newInput.fieldName, embeddable.getInput().fieldName)) || + (newInput.dataViewId && !deepEqual(newInput.dataViewId, embeddable.getInput().dataViewId))) ) { // if the field name or data view id has changed in this editing session, selected options are invalid, so reset them. newInput.value = undefined; diff --git a/src/plugins/controls/public/services/data.ts b/src/plugins/controls/public/services/data.ts index 74c30a1f6d70..4e9db3b68e90 100644 --- a/src/plugins/controls/public/services/data.ts +++ b/src/plugins/controls/public/services/data.ts @@ -24,7 +24,6 @@ export interface ControlsDataService { ) => Observable<{ min?: number; max?: number }>; getDataView: DataPublicPluginStart['dataViews']['get']; getDataView$: (id: string) => Observable; - autocomplete: DataPublicPluginStart['autocomplete']; query: DataPublicPluginStart['query']; searchSource: DataPublicPluginStart['search']['searchSource']; timefilter: DataPublicPluginStart['query']['timefilter']['timefilter']; diff --git a/src/plugins/controls/public/services/index.ts b/src/plugins/controls/public/services/index.ts index cf358dac8b48..ac2e8374ad06 100644 --- a/src/plugins/controls/public/services/index.ts +++ b/src/plugins/controls/public/services/index.ts @@ -12,6 +12,7 @@ import { ControlsOverlaysService } from './overlays'; import { registry as stubRegistry } from './stub'; import { ControlsPluginStart } from '../types'; import { ControlsDataService } from './data'; +import { ControlsUnifiedSearchService } from './unified_search'; import { ControlsService } from './controls'; import { ControlsHTTPService } from './http'; import { ControlsOptionsListService } from './options_list'; @@ -22,6 +23,7 @@ export interface ControlsServices { dataViews: ControlsDataViewsService; overlays: ControlsOverlaysService; data: ControlsDataService; + unifiedSearch: ControlsUnifiedSearchService; http: ControlsHTTPService; settings: ControlsSettingsService; diff --git a/src/plugins/controls/public/services/kibana/data.ts b/src/plugins/controls/public/services/kibana/data.ts index 3a36b929c7cb..29a96a98c7e7 100644 --- a/src/plugins/controls/public/services/kibana/data.ts +++ b/src/plugins/controls/public/services/kibana/data.ts @@ -43,7 +43,7 @@ const minMaxAgg = (field?: DataViewField) => { export const dataServiceFactory: DataServiceFactory = ({ startPlugins }) => { const { - data: { query: queryPlugin, search, autocomplete }, + data: { query: queryPlugin, search }, } = startPlugins; const { data } = startPlugins; @@ -95,7 +95,6 @@ export const dataServiceFactory: DataServiceFactory = ({ startPlugins }) => { from(fetchFieldRange(dataView, fieldName, input)), getDataView: data.dataViews.get, getDataView$: (id: string) => from(data.dataViews.get(id)), - autocomplete, query: queryPlugin, searchSource: search.searchSource, timefilter: queryPlugin.timefilter.timefilter, diff --git a/src/plugins/controls/public/services/kibana/index.ts b/src/plugins/controls/public/services/kibana/index.ts index bed97091bed5..1bb8f600ecf8 100644 --- a/src/plugins/controls/public/services/kibana/index.ts +++ b/src/plugins/controls/public/services/kibana/index.ts @@ -22,6 +22,7 @@ import { dataServiceFactory } from './data'; import { httpServiceFactory } from './http'; import { optionsListServiceFactory } from './options_list'; import { settingsServiceFactory } from './settings'; +import { unifiedSearchServiceFactory } from './unified_search'; export const providers: PluginServiceProviders< ControlsServices, @@ -29,6 +30,7 @@ export const providers: PluginServiceProviders< > = { http: new PluginServiceProvider(httpServiceFactory), data: new PluginServiceProvider(dataServiceFactory), + unifiedSearch: new PluginServiceProvider(unifiedSearchServiceFactory), overlays: new PluginServiceProvider(overlaysServiceFactory), dataViews: new PluginServiceProvider(dataViewsServiceFactory), settings: new PluginServiceProvider(settingsServiceFactory), diff --git a/src/plugins/controls/public/services/kibana/unified_search.ts b/src/plugins/controls/public/services/kibana/unified_search.ts new file mode 100644 index 000000000000..15cb3a02ca45 --- /dev/null +++ b/src/plugins/controls/public/services/kibana/unified_search.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 { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public'; +import { ControlsUnifiedSearchService } from '../unified_search'; +import { ControlsPluginStartDeps } from '../../types'; + +export type UnifiedSearchServiceFactory = KibanaPluginServiceFactory< + ControlsUnifiedSearchService, + ControlsPluginStartDeps +>; + +export const unifiedSearchServiceFactory: UnifiedSearchServiceFactory = ({ startPlugins }) => { + const { + unifiedSearch: { autocomplete }, + } = startPlugins; + + return { + autocomplete, + }; +}; diff --git a/src/plugins/controls/public/services/storybook/data.ts b/src/plugins/controls/public/services/storybook/data.ts index 38a8d01d8c7c..5d8cacad9b7b 100644 --- a/src/plugins/controls/public/services/storybook/data.ts +++ b/src/plugins/controls/public/services/storybook/data.ts @@ -9,20 +9,11 @@ import { of, Observable } from 'rxjs'; import { PluginServiceFactory } from '@kbn/presentation-util-plugin/public'; import { DataPublicPluginStart } from '@kbn/data-plugin/public'; -import { DataViewField, DataView } from '@kbn/data-views-plugin/common'; +import { DataView } from '@kbn/data-views-plugin/common'; import { ControlsDataService } from '../data'; -let valueSuggestionMethod = ({ field, query }: { field: DataViewField; query: string }) => - Promise.resolve(['storybook', 'default', 'values']); -export const replaceValueSuggestionMethod = ( - newMethod: ({ field, query }: { field: DataViewField; query: string }) => Promise -) => (valueSuggestionMethod = newMethod); - export type DataServiceFactory = PluginServiceFactory; export const dataServiceFactory: DataServiceFactory = () => ({ - autocomplete: { - getValueSuggestions: valueSuggestionMethod, - } as unknown as DataPublicPluginStart['autocomplete'], query: {} as unknown as DataPublicPluginStart['query'], searchSource: { create: () => ({ diff --git a/src/plugins/controls/public/services/storybook/index.ts b/src/plugins/controls/public/services/storybook/index.ts index f586063e5a33..751aaeefee9c 100644 --- a/src/plugins/controls/public/services/storybook/index.ts +++ b/src/plugins/controls/public/services/storybook/index.ts @@ -14,6 +14,7 @@ import { } from '@kbn/presentation-util-plugin/public'; import { ControlsServices } from '..'; import { dataServiceFactory } from './data'; +import { unifiedSearchServiceFactory } from './unified_search'; import { overlaysServiceFactory } from './overlays'; import { dataViewsServiceFactory } from './data_views'; import { httpServiceFactory } from '../stub/http'; @@ -28,6 +29,7 @@ export const providers: PluginServiceProviders = { dataViews: new PluginServiceProvider(dataViewsServiceFactory), http: new PluginServiceProvider(httpServiceFactory), data: new PluginServiceProvider(dataServiceFactory), + unifiedSearch: new PluginServiceProvider(unifiedSearchServiceFactory), overlays: new PluginServiceProvider(overlaysServiceFactory), settings: new PluginServiceProvider(settingsServiceFactory), diff --git a/src/plugins/controls/public/services/storybook/unified_search.ts b/src/plugins/controls/public/services/storybook/unified_search.ts new file mode 100644 index 000000000000..cb7df1bd1e48 --- /dev/null +++ b/src/plugins/controls/public/services/storybook/unified_search.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. + */ + +import { PluginServiceFactory } from '@kbn/presentation-util-plugin/public'; +import { DataViewField } from '@kbn/data-views-plugin/common'; +import { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; +import { ControlsUnifiedSearchService } from '../unified_search'; + +let valueSuggestionMethod = ({ field, query }: { field: DataViewField; query: string }) => + Promise.resolve(['storybook', 'default', 'values']); +export const replaceValueSuggestionMethod = ( + newMethod: ({ field, query }: { field: DataViewField; query: string }) => Promise +) => (valueSuggestionMethod = newMethod); + +export type UnifiedSearchServiceFactory = PluginServiceFactory; +export const unifiedSearchServiceFactory: UnifiedSearchServiceFactory = () => ({ + autocomplete: { + getValueSuggestions: valueSuggestionMethod, + } as unknown as UnifiedSearchPublicPluginStart['autocomplete'], +}); diff --git a/src/plugins/controls/public/services/stub/index.ts b/src/plugins/controls/public/services/stub/index.ts index 9b767496fa98..4cd138689452 100644 --- a/src/plugins/controls/public/services/stub/index.ts +++ b/src/plugins/controls/public/services/stub/index.ts @@ -20,6 +20,7 @@ import { dataServiceFactory } from '../storybook/data'; import { dataViewsServiceFactory } from '../storybook/data_views'; import { optionsListServiceFactory } from '../storybook/options_list'; import { settingsServiceFactory } from '../storybook/settings'; +import { unifiedSearchServiceFactory } from '../storybook/unified_search'; export const providers: PluginServiceProviders = { http: new PluginServiceProvider(httpServiceFactory), @@ -27,7 +28,7 @@ export const providers: PluginServiceProviders = { overlays: new PluginServiceProvider(overlaysServiceFactory), dataViews: new PluginServiceProvider(dataViewsServiceFactory), settings: new PluginServiceProvider(settingsServiceFactory), - + unifiedSearch: new PluginServiceProvider(unifiedSearchServiceFactory), controls: new PluginServiceProvider(controlsServiceFactory), optionsList: new PluginServiceProvider(optionsListServiceFactory), }; diff --git a/src/plugins/controls/public/services/unified_search.ts b/src/plugins/controls/public/services/unified_search.ts new file mode 100644 index 000000000000..2c69537b62d6 --- /dev/null +++ b/src/plugins/controls/public/services/unified_search.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. + */ + +import { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; + +export interface ControlsUnifiedSearchService { + autocomplete: UnifiedSearchPublicPluginStart['autocomplete']; +} diff --git a/src/plugins/controls/public/types.ts b/src/plugins/controls/public/types.ts index e5915262223e..d7766eb8d478 100644 --- a/src/plugins/controls/public/types.ts +++ b/src/plugins/controls/public/types.ts @@ -17,6 +17,7 @@ import { } from '@kbn/embeddable-plugin/public'; import { DataPublicPluginStart } from '@kbn/data-plugin/public'; import { DataView, DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; +import { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; import { ControlInput } from '../common/types'; import { ControlsService } from './services/controls'; @@ -70,9 +71,10 @@ export interface ControlsPluginSetupDeps { } export interface ControlsPluginStartDeps { data: DataPublicPluginStart; + unifiedSearch: UnifiedSearchPublicPluginStart; embeddable: EmbeddableStart; dataViews: DataViewsPublicPluginStart; } // re-export from common -export type { ControlWidth, ControlInput, ControlStyle } from '../common/types'; +export type { ControlWidth, ControlInput, DataControlInput, ControlStyle } from '../common/types'; diff --git a/src/plugins/controls/server/control_types/options_list/options_list_suggestions_route.ts b/src/plugins/controls/server/control_types/options_list/options_list_suggestions_route.ts index 70c1db510c53..9af4800ca00f 100644 --- a/src/plugins/controls/server/control_types/options_list/options_list_suggestions_route.ts +++ b/src/plugins/controls/server/control_types/options_list/options_list_suggestions_route.ts @@ -14,8 +14,8 @@ import { Observable } from 'rxjs'; import { CoreSetup, ElasticsearchClient } from '@kbn/core/server'; import { getKbnServerError, reportServerError } from '@kbn/kibana-utils-plugin/server'; -import { PluginSetup as DataPluginSetup } from '@kbn/data-plugin/server'; import { FieldSpec, getFieldSubtypeNested } from '@kbn/data-views-plugin/common'; +import { PluginSetup as UnifiedSearchPluginSetup } from '@kbn/unified-search-plugin/server'; import { OptionsListRequestBody, OptionsListResponse, @@ -23,7 +23,7 @@ import { export const setupOptionsListSuggestionsRoute = ( { http }: CoreSetup, - getAutocompleteSettings: DataPluginSetup['autocomplete']['getAutocompleteSettings'] + getAutocompleteSettings: UnifiedSearchPluginSetup['autocomplete']['getAutocompleteSettings'] ) => { const router = http.createRouter(); @@ -53,7 +53,7 @@ export const setupOptionsListSuggestionsRoute = ( try { const suggestionRequest: OptionsListRequestBody = request.body; const { index } = request.params; - const esClient = context.core.elasticsearch.client.asCurrentUser; + const esClient = (await context.core).elasticsearch.client.asCurrentUser; const suggestionsResponse = await getOptionsListSuggestions({ abortedEvent$: request.events.aborted$, request: suggestionRequest, diff --git a/src/plugins/controls/server/plugin.ts b/src/plugins/controls/server/plugin.ts index 8e391e5f1ac8..cbe9d3923436 100644 --- a/src/plugins/controls/server/plugin.ts +++ b/src/plugins/controls/server/plugin.ts @@ -10,6 +10,7 @@ import { CoreSetup, Plugin } from '@kbn/core/server'; import { EmbeddableSetup } from '@kbn/embeddable-plugin/server'; import { PluginSetup as DataSetup } from '@kbn/data-plugin/server'; +import { PluginSetup as UnifiedSearchSetup } from '@kbn/unified-search-plugin/server'; import { setupOptionsListSuggestionsRoute } from './control_types/options_list/options_list_suggestions_route'; import { controlGroupContainerPersistableStateServiceFactory } from './control_group/control_group_container_factory'; import { optionsListPersistableStateServiceFactory } from './control_types/options_list/options_list_embeddable_factory'; @@ -18,10 +19,11 @@ import { timeSliderPersistableStateServiceFactory } from './control_types/time_s interface SetupDeps { embeddable: EmbeddableSetup; data: DataSetup; + unifiedSearch: UnifiedSearchSetup; } export class ControlsPlugin implements Plugin { - public setup(core: CoreSetup, { embeddable, data }: SetupDeps) { + public setup(core: CoreSetup, { embeddable, unifiedSearch }: SetupDeps) { embeddable.registerEmbeddableFactory(optionsListPersistableStateServiceFactory()); embeddable.registerEmbeddableFactory(timeSliderPersistableStateServiceFactory()); @@ -29,7 +31,7 @@ export class ControlsPlugin implements Plugin { controlGroupContainerPersistableStateServiceFactory(embeddable) ); - setupOptionsListSuggestionsRoute(core, data.autocomplete.getAutocompleteSettings); + setupOptionsListSuggestionsRoute(core, unifiedSearch.autocomplete.getAutocompleteSettings); return {}; } diff --git a/src/plugins/controls/tsconfig.json b/src/plugins/controls/tsconfig.json index 10ec84fbd32f..5a17afc93134 100644 --- a/src/plugins/controls/tsconfig.json +++ b/src/plugins/controls/tsconfig.json @@ -23,6 +23,7 @@ { "path": "../embeddable/tsconfig.json" }, { "path": "../presentation_util/tsconfig.json" }, { "path": "../kibana_react/tsconfig.json" }, - { "path": "../data/tsconfig.json" } + { "path": "../data/tsconfig.json" }, + { "path": "../unified_search/tsconfig.json" } ] } diff --git a/src/plugins/data/config.ts b/src/plugins/data/config.ts index 251284655410..c419683de986 100644 --- a/src/plugins/data/config.ts +++ b/src/plugins/data/config.ts @@ -9,28 +9,6 @@ import { schema, TypeOf } from '@kbn/config-schema'; export const configSchema = schema.object({ - autocomplete: schema.object({ - querySuggestions: schema.object({ - enabled: schema.boolean({ defaultValue: true }), - }), - valueSuggestions: schema.object({ - enabled: schema.boolean({ defaultValue: true }), - tiers: schema.arrayOf( - schema.oneOf([ - schema.literal('data_content'), - schema.literal('data_hot'), - schema.literal('data_warm'), - schema.literal('data_cold'), - schema.literal('data_frozen'), - ]), - { - defaultValue: ['data_hot', 'data_warm', 'data_content', 'data_cold'], - } - ), - terminateAfter: schema.duration({ defaultValue: 100000 }), - timeout: schema.duration({ defaultValue: 1000 }), - }), - }), search: schema.object({ aggs: schema.object({ shardDelay: schema.object({ diff --git a/src/plugins/data/kibana.json b/src/plugins/data/kibana.json index e3369c2d571a..fa7453d4c5bd 100644 --- a/src/plugins/data/kibana.json +++ b/src/plugins/data/kibana.json @@ -4,7 +4,7 @@ "server": true, "ui": true, "requiredPlugins": ["bfetch", "expressions", "uiActions", "share", "inspector", "fieldFormats", "dataViews"], - "serviceFolders": ["search", "query", "autocomplete", "ui"], + "serviceFolders": ["search", "query", "ui"], "optionalPlugins": ["usageCollection", "taskManager", "security"], "extraPublicDirs": ["common"], "requiredBundles": ["kibanaUtils", "kibanaReact", "inspector"], diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts index 1f2b00313ddb..9f1bf02ec2cd 100644 --- a/src/plugins/data/public/index.ts +++ b/src/plugins/data/public/index.ts @@ -95,20 +95,6 @@ export { DuplicateDataViewError, } from '../common'; -/* - * Autocomplete query suggestions: - */ - -export type { - QuerySuggestion, - QuerySuggestionGetFn, - QuerySuggestionGetFnArgs, - QuerySuggestionBasic, - QuerySuggestionField, - AutocompleteStart, -} from './autocomplete'; - -export { QuerySuggestionTypes } from './autocomplete'; /* * Search: */ @@ -273,8 +259,12 @@ export { TimeHistory, getQueryLog, mapAndFlattenFilters, + QueryService, } from './query'; +export { NowProvider } from './now_provider'; +export type { NowProviderInternalContract } from './now_provider'; + export type { QueryState, SavedQuery, @@ -288,6 +278,7 @@ export type { PersistedLog, QueryStringContract, QuerySetup, + TimefilterSetup, } from './query'; export type { AggsStart } from './search/aggs'; diff --git a/src/plugins/data/public/mocks.ts b/src/plugins/data/public/mocks.ts index af6fdedc3ed9..27e365ce0cb3 100644 --- a/src/plugins/data/public/mocks.ts +++ b/src/plugins/data/public/mocks.ts @@ -11,27 +11,14 @@ import { createDatatableUtilitiesMock } from '../common/mocks'; import { DataPlugin, DataViewsContract } from '.'; import { searchServiceMock } from './search/mocks'; import { queryServiceMock } from './query/mocks'; -import { AutocompleteStart, AutocompleteSetup } from './autocomplete'; import { createNowProviderMock } from './now_provider/mocks'; export type Setup = jest.Mocked>; export type Start = jest.Mocked>; -const autocompleteSetupMock: jest.Mocked = { - getQuerySuggestions: jest.fn(), - getAutocompleteSettings: jest.fn(), -}; - -const autocompleteStartMock: jest.Mocked = { - getValueSuggestions: jest.fn(), - getQuerySuggestions: jest.fn(), - hasQuerySuggestions: jest.fn(), -}; - const createSetupContract = (): Setup => { const querySetupMock = queryServiceMock.createSetupContract(); return { - autocomplete: autocompleteSetupMock, search: searchServiceMock.createSetupContract(), query: querySetupMock, }; @@ -58,7 +45,6 @@ const createStartContract = (): Start => { createFiltersFromValueClickAction: jest.fn().mockResolvedValue(['yes']), createFiltersFromRangeSelectAction: jest.fn(), }, - autocomplete: autocompleteStartMock, datatableUtilities: createDatatableUtilitiesMock(), search: searchServiceMock.createStartContract(), fieldFormats: fieldFormatsServiceMock.createStartContract(), diff --git a/src/plugins/data/public/plugin.ts b/src/plugins/data/public/plugin.ts index ed9ba5c00e49..175d05870b0c 100644 --- a/src/plugins/data/public/plugin.ts +++ b/src/plugins/data/public/plugin.ts @@ -21,7 +21,6 @@ import type { DataSetupDependencies, DataStartDependencies, } from './types'; -import { AutocompleteService } from './autocomplete'; import { SearchService } from './search/search_service'; import { QueryService } from './query'; import { @@ -52,7 +51,6 @@ export class DataPublicPlugin DataStartDependencies > { - private readonly autocomplete: AutocompleteService; private readonly searchService: SearchService; private readonly queryService: QueryService; private readonly storage: IStorageWrapper; @@ -62,7 +60,6 @@ export class DataPublicPlugin this.searchService = new SearchService(initializerContext); this.queryService = new QueryService(); - this.autocomplete = new AutocompleteService(initializerContext); this.storage = new Storage(window.localStorage); this.nowProvider = new NowProvider(); } @@ -113,10 +110,6 @@ export class DataPublicPlugin ); return { - autocomplete: this.autocomplete.setup(core, { - timefilter: queryService.timefilter, - usageCollection, - }), search: searchService, query: queryService, }; @@ -161,7 +154,6 @@ export class DataPublicPlugin createFiltersFromValueClickAction, createFiltersFromRangeSelectAction, }, - autocomplete: this.autocomplete.start(), datatableUtilities, fieldFormats, indexPatterns: dataViews, @@ -175,7 +167,6 @@ export class DataPublicPlugin } public stop() { - this.autocomplete.clearProviders(); this.queryService.stop(); this.searchService.stop(); } diff --git a/src/plugins/data/public/types.ts b/src/plugins/data/public/types.ts index 4f571559df8d..b596e02e7f2a 100644 --- a/src/plugins/data/public/types.ts +++ b/src/plugins/data/public/types.ts @@ -16,7 +16,6 @@ import { FieldFormatsSetup, FieldFormatsStart } from '@kbn/field-formats-plugin/ import { UsageCollectionSetup, UsageCollectionStart } from '@kbn/usage-collection-plugin/public'; import { Setup as InspectorSetup } from '@kbn/inspector-plugin/public'; import { DatatableUtilitiesService } from '../common'; -import { AutocompleteSetup, AutocompleteStart } from './autocomplete'; import { createFiltersFromRangeSelectAction, createFiltersFromValueClickAction } from './actions'; import type { ISearchSetup, ISearchStart } from './search'; import { QuerySetup, QueryStart } from './query'; @@ -42,7 +41,6 @@ export interface DataStartDependencies { * Data plugin public Setup contract */ export interface DataPublicPluginSetup { - autocomplete: AutocompleteSetup; search: ISearchSetup; query: QuerySetup; } @@ -64,11 +62,6 @@ export interface DataPublicPluginStart { * {@link DataPublicPluginStartActions} */ actions: DataPublicPluginStartActions; - /** - * autocomplete service - * {@link AutocompleteStart} - */ - autocomplete: AutocompleteStart; /** * data views service * {@link DataViewsContract} diff --git a/src/plugins/data/server/index.ts b/src/plugins/data/server/index.ts index 83fcd58ba9a4..b970221c4339 100644 --- a/src/plugins/data/server/index.ts +++ b/src/plugins/data/server/index.ts @@ -16,6 +16,8 @@ import { DataServerPlugin, DataPluginSetup, DataPluginStart } from './plugin'; export * from './deprecated'; export { getEsQueryConfig, DEFAULT_QUERY_LANGUAGE } from '../common'; +export { getRequestAbortedSignal } from './lib'; + /** * Exporters (CSV) */ @@ -68,7 +70,6 @@ import { // tabify calcAutoIntervalLessThan, } from '../common'; -import { autocompleteConfigDeprecationProvider } from './config_deprecations'; export type { ParsedInterval, @@ -121,9 +122,7 @@ export type { DataPluginSetup as PluginSetup, DataPluginStart as PluginStart }; export { DataServerPlugin as Plugin }; export const config: PluginConfigDescriptor = { - deprecations: autocompleteConfigDeprecationProvider, exposeToBrowser: { - autocomplete: true, search: true, }, schema: configSchema, diff --git a/src/plugins/data/server/mocks.ts b/src/plugins/data/server/mocks.ts index 477ccf312032..8df65f7e5b90 100644 --- a/src/plugins/data/server/mocks.ts +++ b/src/plugins/data/server/mocks.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import { coreMock } from '@kbn/core/server/mocks'; import { createFieldFormatsSetupMock, createFieldFormatsStartMock, @@ -17,17 +18,10 @@ import { } from './search/mocks'; import { createIndexPatternsStartMock } from './data_views/mocks'; import { createDatatableUtilitiesMock } from './datatable_utilities/mock'; -import { DataRequestHandlerContext } from './search'; -import { AutocompleteSetup } from './autocomplete'; - -const autocompleteSetupMock: jest.Mocked = { - getAutocompleteSettings: jest.fn(), -}; function createSetupContract() { return { search: createSearchSetupMock(), - autocomplete: autocompleteSetupMock, /** * @deprecated - use directly from "fieldFormats" plugin instead */ @@ -49,8 +43,9 @@ function createStartContract() { function createRequestHandlerContext() { return { + core: coreMock.createRequestHandlerContext(), search: createSearchRequestHandlerContext(), - } as unknown as jest.Mocked; + }; } export const dataPluginMock = { diff --git a/src/plugins/data/server/plugin.ts b/src/plugins/data/server/plugin.ts index dcf0baa188ea..d1beb335dd35 100644 --- a/src/plugins/data/server/plugin.ts +++ b/src/plugins/data/server/plugin.ts @@ -19,17 +19,14 @@ import { SearchService } from './search/search_service'; import { QueryService } from './query/query_service'; import { ScriptsService } from './scripts'; import { KqlTelemetryService } from './kql_telemetry'; -import { AutocompleteService } from './autocomplete'; import { getUiSettings } from './ui_settings'; import { QuerySetup } from './query'; -import { AutocompleteSetup } from './autocomplete/autocomplete_service'; interface DataEnhancements { search: SearchEnhancements; } export interface DataPluginSetup { - autocomplete: AutocompleteSetup; search: ISearchSetup; query: QuerySetup; /** @@ -81,7 +78,6 @@ export class DataServerPlugin private readonly searchService: SearchService; private readonly scriptsService: ScriptsService; private readonly kqlTelemetryService: KqlTelemetryService; - private readonly autocompleteService: AutocompleteService; private readonly queryService = new QueryService(); private readonly logger: Logger; @@ -90,7 +86,6 @@ export class DataServerPlugin this.searchService = new SearchService(initializerContext, this.logger); this.scriptsService = new ScriptsService(); this.kqlTelemetryService = new KqlTelemetryService(initializerContext); - this.autocompleteService = new AutocompleteService(initializerContext); } public setup( @@ -110,7 +105,6 @@ export class DataServerPlugin }); return { - autocomplete: this.autocompleteService.setup(core), __enhance: (enhancements: DataEnhancements) => { searchSetup.__enhance(enhancements.search); }, diff --git a/src/plugins/data/server/query/route_handler_context.test.ts b/src/plugins/data/server/query/route_handler_context.test.ts index 69908f9733f5..c0db432ce3fd 100644 --- a/src/plugins/data/server/query/route_handler_context.test.ts +++ b/src/plugins/data/server/query/route_handler_context.test.ts @@ -20,7 +20,6 @@ const { savedObjects: { client: mockSavedObjectsClient }, }, } = mockContext; -const context = registerSavedQueryRouteHandlerContext(mockContext); const savedQueryAttributes: SavedQueryAttributes = { title: 'foo', @@ -73,7 +72,15 @@ const savedQueryReferences = [ ]; describe('saved query route handler context', () => { - beforeEach(() => { + let context: Awaited>; + + beforeEach(async () => { + context = await registerSavedQueryRouteHandlerContext( + coreMock.createCustomRequestHandlerContext({ + core: mockContext.core, + }) + ); + mockSavedObjectsClient.create.mockClear(); mockSavedObjectsClient.resolve.mockClear(); mockSavedObjectsClient.find.mockClear(); diff --git a/src/plugins/data/server/query/route_handler_context.ts b/src/plugins/data/server/query/route_handler_context.ts index e72257fe185d..0af658d7692c 100644 --- a/src/plugins/data/server/query/route_handler_context.ts +++ b/src/plugins/data/server/query/route_handler_context.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { RequestHandlerContext, SavedObject } from '@kbn/core/server'; +import { CustomRequestHandlerContext, RequestHandlerContext, SavedObject } from '@kbn/core/server'; import { isFilters } from '@kbn/es-query'; import { isQuery, SavedQueryAttributes } from '../../common'; import { extract, inject } from '../../common/query/persistable_state'; @@ -66,18 +66,16 @@ function verifySavedQuery({ title, query, filters = [] }: SavedQueryAttributes) } } -export function registerSavedQueryRouteHandlerContext(context: RequestHandlerContext) { +export async function registerSavedQueryRouteHandlerContext(context: RequestHandlerContext) { + const soClient = (await context.core).savedObjects.client; + const createSavedQuery = async (attrs: SavedQueryAttributes) => { verifySavedQuery(attrs); const { attributes, references } = extractReferences(attrs); - const savedObject = await context.core.savedObjects.client.create( - 'query', - attributes, - { - references, - } - ); + const savedObject = await soClient.create('query', attributes, { + references, + }); // TODO: Handle properly if (savedObject.error) throw new Error(savedObject.error.message); @@ -89,14 +87,9 @@ export function registerSavedQueryRouteHandlerContext(context: RequestHandlerCon verifySavedQuery(attrs); const { attributes, references } = extractReferences(attrs); - const savedObject = await context.core.savedObjects.client.update( - 'query', - id, - attributes, - { - references, - } - ); + const savedObject = await soClient.update('query', id, attributes, { + references, + }); // TODO: Handle properly if (savedObject.error) throw new Error(savedObject.error.message); @@ -105,8 +98,10 @@ export function registerSavedQueryRouteHandlerContext(context: RequestHandlerCon }; const getSavedQuery = async (id: string) => { - const { saved_object: savedObject, outcome } = - await context.core.savedObjects.client.resolve('query', id); + const { saved_object: savedObject, outcome } = await soClient.resolve( + 'query', + id + ); if (outcome === 'conflict') { throw new Error(`Multiple saved queries found with ID: ${id} (legacy URL alias conflict)`); } else if (savedObject.error) { @@ -116,20 +111,19 @@ export function registerSavedQueryRouteHandlerContext(context: RequestHandlerCon }; const getSavedQueriesCount = async () => { - const { total } = await context.core.savedObjects.client.find({ + const { total } = await soClient.find({ type: 'query', }); return total; }; const findSavedQueries = async ({ page = 1, perPage = 50, search = '' } = {}) => { - const { total, saved_objects: savedObjects } = - await context.core.savedObjects.client.find({ - type: 'query', - page, - perPage, - search, - }); + const { total, saved_objects: savedObjects } = await soClient.find({ + type: 'query', + page, + perPage, + search, + }); const savedQueries = savedObjects.map(injectReferences); @@ -137,7 +131,7 @@ export function registerSavedQueryRouteHandlerContext(context: RequestHandlerCon }; const getAllSavedQueries = async () => { - const finder = context.core.savedObjects.client.createPointInTimeFinder({ + const finder = soClient.createPointInTimeFinder({ type: 'query', perPage: 100, }); @@ -152,8 +146,8 @@ export function registerSavedQueryRouteHandlerContext(context: RequestHandlerCon return { total: savedQueries.length, savedQueries }; }; - const deleteSavedQuery = (id: string) => { - return context.core.savedObjects.client.delete('query', id); + const deleteSavedQuery = async (id: string) => { + return await soClient.delete('query', id); }; return { @@ -167,6 +161,6 @@ export function registerSavedQueryRouteHandlerContext(context: RequestHandlerCon }; } -export interface SavedQueryRouteHandlerContext extends RequestHandlerContext { - savedQuery: ReturnType; -} +export type SavedQueryRouteHandlerContext = CustomRequestHandlerContext<{ + savedQuery: Promise>; +}>; diff --git a/src/plugins/data/server/query/routes.ts b/src/plugins/data/server/query/routes.ts index d5997e93fa99..16f5d8f9955a 100644 --- a/src/plugins/data/server/query/routes.ts +++ b/src/plugins/data/server/query/routes.ts @@ -37,7 +37,8 @@ export function registerSavedQueryRoutes({ http }: CoreSetup): void { }, async (context, request, response) => { try { - const body = await context.savedQuery.create(request.body); + const savedQuery = await context.savedQuery; + const body = await savedQuery.create(request.body); return response.ok({ body }); } catch (e) { // TODO: Handle properly @@ -57,7 +58,8 @@ export function registerSavedQueryRoutes({ http }: CoreSetup): void { async (context, request, response) => { const { id } = request.params; try { - const body = await context.savedQuery.update(id, request.body); + const savedQuery = await context.savedQuery; + const body = await savedQuery.update(id, request.body); return response.ok({ body }); } catch (e) { // TODO: Handle properly @@ -76,7 +78,8 @@ export function registerSavedQueryRoutes({ http }: CoreSetup): void { async (context, request, response) => { const { id } = request.params; try { - const body = await context.savedQuery.get(id); + const savedQuery = await context.savedQuery; + const body = await savedQuery.get(id); return response.ok({ body }); } catch (e) { // TODO: Handle properly @@ -92,7 +95,8 @@ export function registerSavedQueryRoutes({ http }: CoreSetup): void { }, async (context, request, response) => { try { - const count = await context.savedQuery.count(); + const savedQuery = await context.savedQuery; + const count = await savedQuery.count(); return response.ok({ body: `${count}` }); } catch (e) { // TODO: Handle properly @@ -114,7 +118,8 @@ export function registerSavedQueryRoutes({ http }: CoreSetup): void { }, async (context, request, response) => { try { - const body = await context.savedQuery.find(request.body); + const savedQuery = await context.savedQuery; + const body = await savedQuery.find(request.body); return response.ok({ body }); } catch (e) { // TODO: Handle properly @@ -130,7 +135,8 @@ export function registerSavedQueryRoutes({ http }: CoreSetup): void { }, async (context, request, response) => { try { - const body = await context.savedQuery.getAll(); + const savedQuery = await context.savedQuery; + const body = await savedQuery.getAll(); return response.ok({ body }); } catch (e) { // TODO: Handle properly @@ -149,7 +155,8 @@ export function registerSavedQueryRoutes({ http }: CoreSetup): void { async (context, request, response) => { const { id } = request.params; try { - const body = await context.savedQuery.delete(id); + const savedQuery = await context.savedQuery; + const body = await savedQuery.delete(id); return response.ok({ body }); } catch (e) { // TODO: Handle properly diff --git a/src/plugins/data/server/search/routes/search.ts b/src/plugins/data/server/search/routes/search.ts index 6f48ba747a71..aa0f69b55816 100644 --- a/src/plugins/data/server/search/routes/search.ts +++ b/src/plugins/data/server/search/routes/search.ts @@ -47,8 +47,9 @@ export function registerSearchRoute(router: DataPluginRouter): void { const abortSignal = getRequestAbortedSignal(request.events.aborted$); try { - const response = await context - .search!.search( + const search = await context.search; + const response = await search + .search( { ...searchRequest, id }, { abortSignal, @@ -85,7 +86,8 @@ export function registerSearchRoute(router: DataPluginRouter): void { const { strategy, id } = request.params; try { - await context.search!.cancel(id, { strategy }); + const search = await context.search; + await search.cancel(id, { strategy }); return res.ok(); } catch (err) { return reportServerError(res, err); diff --git a/src/plugins/data/server/search/search_service.ts b/src/plugins/data/server/search/search_service.ts index 8c264889ebf2..c59893cdead9 100644 --- a/src/plugins/data/server/search/search_service.ts +++ b/src/plugins/data/server/search/search_service.ts @@ -143,7 +143,7 @@ export class SearchService implements Plugin { core.http.registerRouteHandlerContext( 'search', - async (context, request) => { + (context, request) => { return this.asScoped(request); } ); diff --git a/src/plugins/data/server/search/types.ts b/src/plugins/data/server/search/types.ts index ef6a2f5f88fe..70e04e97ea16 100644 --- a/src/plugins/data/server/search/types.ts +++ b/src/plugins/data/server/search/types.ts @@ -13,7 +13,7 @@ import type { IUiSettingsClient, SavedObjectsClientContract, KibanaRequest, - RequestHandlerContext, + CustomRequestHandlerContext, } from '@kbn/core/server'; import { ISearchOptions, @@ -126,8 +126,8 @@ export interface ISearchStart< export type SearchRequestHandlerContext = IScopedSearchClient; -export interface DataRequestHandlerContext extends RequestHandlerContext { +export type DataRequestHandlerContext = CustomRequestHandlerContext<{ search: SearchRequestHandlerContext; -} +}>; export type DataPluginRouter = IRouter; diff --git a/src/plugins/data_view_field_editor/server/routes/field_preview.ts b/src/plugins/data_view_field_editor/server/routes/field_preview.ts index 022cd92a4bb1..5595eeeaeddf 100644 --- a/src/plugins/data_view_field_editor/server/routes/field_preview.ts +++ b/src/plugins/data_view_field_editor/server/routes/field_preview.ts @@ -38,7 +38,7 @@ export const registerFieldPreviewRoute = ({ router }: RouteDependencies): void = }, }, async (ctx, req, res) => { - const { client } = ctx.core.elasticsearch; + const { client } = (await ctx.core).elasticsearch; const type = req.body.context.split('_field')[0] as estypes.MappingRuntimeFieldType; const body = { diff --git a/src/plugins/data_view_management/server/routes/preview_scripted_field.ts b/src/plugins/data_view_management/server/routes/preview_scripted_field.ts index aee953af1c29..3a19872150a8 100644 --- a/src/plugins/data_view_management/server/routes/preview_scripted_field.ts +++ b/src/plugins/data_view_management/server/routes/preview_scripted_field.ts @@ -24,7 +24,7 @@ export function registerPreviewScriptedFieldRoute(router: IRouter): void { }, }, async (context, request, res) => { - const client = context.core.elasticsearch.client.asCurrentUser; + const client = (await context.core).elasticsearch.client.asCurrentUser; const { index, name, script, query, additionalFields } = request.body; try { diff --git a/src/plugins/data_view_management/server/routes/resolve_index.ts b/src/plugins/data_view_management/server/routes/resolve_index.ts index 85ad6ae8993a..820e6de1c9d7 100644 --- a/src/plugins/data_view_management/server/routes/resolve_index.ts +++ b/src/plugins/data_view_management/server/routes/resolve_index.ts @@ -31,7 +31,8 @@ export function registerResolveIndexRoute(router: IRouter): void { }, }, async (context, req, res) => { - const body = await context.core.elasticsearch.client.asCurrentUser.indices.resolveIndex({ + const esClient = (await context.core).elasticsearch.client; + const body = await esClient.asCurrentUser.indices.resolveIndex({ name: req.params.query, expand_wildcards: req.query.expand_wildcards || 'open', }); diff --git a/src/plugins/data_views/server/rest_api_routes/create_data_view.ts b/src/plugins/data_views/server/rest_api_routes/create_data_view.ts index 755ba5dcd86e..c35344f54c4f 100644 --- a/src/plugins/data_views/server/rest_api_routes/create_data_view.ts +++ b/src/plugins/data_views/server/rest_api_routes/create_data_view.ts @@ -94,8 +94,9 @@ const registerCreateDataViewRouteFactory = }, router.handleLegacyErrors( handleErrors(async (ctx, req, res) => { - const savedObjectsClient = ctx.core.savedObjects.client; - const elasticsearchClient = ctx.core.elasticsearch.client.asCurrentUser; + const core = await ctx.core; + const savedObjectsClient = core.savedObjects.client; + const elasticsearchClient = core.elasticsearch.client.asCurrentUser; const [, , { dataViewsServiceFactory }] = await getStartServices(); const dataViewsService = await dataViewsServiceFactory( diff --git a/src/plugins/data_views/server/rest_api_routes/default_data_view.ts b/src/plugins/data_views/server/rest_api_routes/default_data_view.ts index da89bbe95b69..7ce241176228 100644 --- a/src/plugins/data_views/server/rest_api_routes/default_data_view.ts +++ b/src/plugins/data_views/server/rest_api_routes/default_data_view.ts @@ -64,8 +64,9 @@ const manageDefaultIndexPatternRoutesFactory = validate: {}, }, handleErrors(async (ctx, req, res) => { - const savedObjectsClient = ctx.core.savedObjects.client; - const elasticsearchClient = ctx.core.elasticsearch.client.asCurrentUser; + const core = await ctx.core; + const savedObjectsClient = core.savedObjects.client; + const elasticsearchClient = core.elasticsearch.client.asCurrentUser; const [, , { dataViewsServiceFactory }] = await getStartServices(); const dataViewsService = await dataViewsServiceFactory( savedObjectsClient, @@ -103,8 +104,9 @@ const manageDefaultIndexPatternRoutesFactory = }, }, handleErrors(async (ctx, req, res) => { - const savedObjectsClient = ctx.core.savedObjects.client; - const elasticsearchClient = ctx.core.elasticsearch.client.asCurrentUser; + const core = await ctx.core; + const savedObjectsClient = core.savedObjects.client; + const elasticsearchClient = core.elasticsearch.client.asCurrentUser; const [, , { dataViewsServiceFactory }] = await getStartServices(); const dataViewsService = await dataViewsServiceFactory( savedObjectsClient, diff --git a/src/plugins/data_views/server/rest_api_routes/delete_data_view.ts b/src/plugins/data_views/server/rest_api_routes/delete_data_view.ts index 88370b5aea77..b3fd0b8a041c 100644 --- a/src/plugins/data_views/server/rest_api_routes/delete_data_view.ts +++ b/src/plugins/data_views/server/rest_api_routes/delete_data_view.ts @@ -58,8 +58,9 @@ const deleteIndexPatternRouteFactory = }, router.handleLegacyErrors( handleErrors(async (ctx, req, res) => { - const savedObjectsClient = ctx.core.savedObjects.client; - const elasticsearchClient = ctx.core.elasticsearch.client.asCurrentUser; + const core = await ctx.core; + const savedObjectsClient = core.savedObjects.client; + const elasticsearchClient = core.elasticsearch.client.asCurrentUser; const [, , { dataViewsServiceFactory }] = await getStartServices(); const dataViewsService = await dataViewsServiceFactory( savedObjectsClient, diff --git a/src/plugins/data_views/server/rest_api_routes/fields/update_fields.ts b/src/plugins/data_views/server/rest_api_routes/fields/update_fields.ts index 49e3d3fc0f1e..d99aa67c7f15 100644 --- a/src/plugins/data_views/server/rest_api_routes/fields/update_fields.ts +++ b/src/plugins/data_views/server/rest_api_routes/fields/update_fields.ts @@ -134,8 +134,9 @@ const updateFieldsActionRouteFactory = (path: string, serviceKey: string) => { }, router.handleLegacyErrors( handleErrors(async (ctx, req, res) => { - const savedObjectsClient = ctx.core.savedObjects.client; - const elasticsearchClient = ctx.core.elasticsearch.client.asCurrentUser; + const core = await ctx.core; + const savedObjectsClient = core.savedObjects.client; + const elasticsearchClient = core.elasticsearch.client.asCurrentUser; const [, , { dataViewsServiceFactory }] = await getStartServices(); const dataViewsService = await dataViewsServiceFactory( savedObjectsClient, diff --git a/src/plugins/data_views/server/rest_api_routes/get_data_view.ts b/src/plugins/data_views/server/rest_api_routes/get_data_view.ts index 63ef2701a306..86185fb0b277 100644 --- a/src/plugins/data_views/server/rest_api_routes/get_data_view.ts +++ b/src/plugins/data_views/server/rest_api_routes/get_data_view.ts @@ -63,8 +63,9 @@ const getDataViewRouteFactory = }, router.handleLegacyErrors( handleErrors(async (ctx, req, res) => { - const savedObjectsClient = ctx.core.savedObjects.client; - const elasticsearchClient = ctx.core.elasticsearch.client.asCurrentUser; + const core = await ctx.core; + const savedObjectsClient = core.savedObjects.client; + const elasticsearchClient = core.elasticsearch.client.asCurrentUser; const [, , { dataViewsServiceFactory }] = await getStartServices(); const dataViewsService = await dataViewsServiceFactory( savedObjectsClient, diff --git a/src/plugins/data_views/server/rest_api_routes/has_user_data_view.ts b/src/plugins/data_views/server/rest_api_routes/has_user_data_view.ts index 5bbe7966ae26..1031a04456b4 100644 --- a/src/plugins/data_views/server/rest_api_routes/has_user_data_view.ts +++ b/src/plugins/data_views/server/rest_api_routes/has_user_data_view.ts @@ -45,8 +45,9 @@ const hasUserDataViewRouteFactory = }, router.handleLegacyErrors( handleErrors(async (ctx, req, res) => { - const savedObjectsClient = ctx.core.savedObjects.client; - const elasticsearchClient = ctx.core.elasticsearch.client.asCurrentUser; + const core = await ctx.core; + const savedObjectsClient = core.savedObjects.client; + const elasticsearchClient = core.elasticsearch.client.asCurrentUser; const [, , { dataViewsServiceFactory }] = await getStartServices(); const dataViewsService = await dataViewsServiceFactory( diff --git a/src/plugins/data_views/server/rest_api_routes/runtime_fields/create_runtime_field.ts b/src/plugins/data_views/server/rest_api_routes/runtime_fields/create_runtime_field.ts index 4ae100e1a79f..5c9faaf2c859 100644 --- a/src/plugins/data_views/server/rest_api_routes/runtime_fields/create_runtime_field.ts +++ b/src/plugins/data_views/server/rest_api_routes/runtime_fields/create_runtime_field.ts @@ -92,8 +92,9 @@ const runtimeCreateFieldRouteFactory = }, }, handleErrors(async (ctx, req, res) => { - const savedObjectsClient = ctx.core.savedObjects.client; - const elasticsearchClient = ctx.core.elasticsearch.client.asCurrentUser; + const core = await ctx.core; + const savedObjectsClient = core.savedObjects.client; + const elasticsearchClient = core.elasticsearch.client.asCurrentUser; const [, , { dataViewsServiceFactory }] = await getStartServices(); const dataViewsService = await dataViewsServiceFactory( savedObjectsClient, diff --git a/src/plugins/data_views/server/rest_api_routes/runtime_fields/delete_runtime_field.ts b/src/plugins/data_views/server/rest_api_routes/runtime_fields/delete_runtime_field.ts index 94dde735c067..e7db34f43cf3 100644 --- a/src/plugins/data_views/server/rest_api_routes/runtime_fields/delete_runtime_field.ts +++ b/src/plugins/data_views/server/rest_api_routes/runtime_fields/delete_runtime_field.ts @@ -73,8 +73,9 @@ const deleteRuntimeFieldRouteFactory = }, }, handleErrors(async (ctx, req, res) => { - const savedObjectsClient = ctx.core.savedObjects.client; - const elasticsearchClient = ctx.core.elasticsearch.client.asCurrentUser; + const core = await ctx.core; + const savedObjectsClient = core.savedObjects.client; + const elasticsearchClient = core.elasticsearch.client.asCurrentUser; const [, , { dataViewsServiceFactory }] = await getStartServices(); const dataViewsService = await dataViewsServiceFactory( savedObjectsClient, diff --git a/src/plugins/data_views/server/rest_api_routes/runtime_fields/get_runtime_field.ts b/src/plugins/data_views/server/rest_api_routes/runtime_fields/get_runtime_field.ts index 4d9a5678392a..867766eec312 100644 --- a/src/plugins/data_views/server/rest_api_routes/runtime_fields/get_runtime_field.ts +++ b/src/plugins/data_views/server/rest_api_routes/runtime_fields/get_runtime_field.ts @@ -80,8 +80,9 @@ const getRuntimeFieldRouteFactory = }, handleErrors(async (ctx, req, res) => { - const savedObjectsClient = ctx.core.savedObjects.client; - const elasticsearchClient = ctx.core.elasticsearch.client.asCurrentUser; + const core = await ctx.core; + const savedObjectsClient = core.savedObjects.client; + const elasticsearchClient = core.elasticsearch.client.asCurrentUser; const [, , { dataViewsServiceFactory }] = await getStartServices(); const dataViewsService = await dataViewsServiceFactory( savedObjectsClient, diff --git a/src/plugins/data_views/server/rest_api_routes/runtime_fields/put_runtime_field.ts b/src/plugins/data_views/server/rest_api_routes/runtime_fields/put_runtime_field.ts index ed4f01d703f0..0f5399606dfd 100644 --- a/src/plugins/data_views/server/rest_api_routes/runtime_fields/put_runtime_field.ts +++ b/src/plugins/data_views/server/rest_api_routes/runtime_fields/put_runtime_field.ts @@ -92,8 +92,9 @@ const putRuntimeFieldRouteFactory = }, }, handleErrors(async (ctx, req, res) => { - const savedObjectsClient = ctx.core.savedObjects.client; - const elasticsearchClient = ctx.core.elasticsearch.client.asCurrentUser; + const core = await ctx.core; + const savedObjectsClient = core.savedObjects.client; + const elasticsearchClient = core.elasticsearch.client.asCurrentUser; const [, , { dataViewsServiceFactory }] = await getStartServices(); const dataViewsService = await dataViewsServiceFactory( savedObjectsClient, diff --git a/src/plugins/data_views/server/rest_api_routes/runtime_fields/update_runtime_field.ts b/src/plugins/data_views/server/rest_api_routes/runtime_fields/update_runtime_field.ts index fe632f5533dc..1aaf1b112fee 100644 --- a/src/plugins/data_views/server/rest_api_routes/runtime_fields/update_runtime_field.ts +++ b/src/plugins/data_views/server/rest_api_routes/runtime_fields/update_runtime_field.ts @@ -93,8 +93,9 @@ const updateRuntimeFieldRouteFactory = }, }, handleErrors(async (ctx, req, res) => { - const savedObjectsClient = ctx.core.savedObjects.client; - const elasticsearchClient = ctx.core.elasticsearch.client.asCurrentUser; + const core = await ctx.core; + const savedObjectsClient = core.savedObjects.client; + const elasticsearchClient = core.elasticsearch.client.asCurrentUser; const [, , { dataViewsServiceFactory }] = await getStartServices(); const dataViewsService = await dataViewsServiceFactory( savedObjectsClient, diff --git a/src/plugins/data_views/server/rest_api_routes/scripted_fields/create_scripted_field.ts b/src/plugins/data_views/server/rest_api_routes/scripted_fields/create_scripted_field.ts index 4335fc6aea90..759627f92676 100644 --- a/src/plugins/data_views/server/rest_api_routes/scripted_fields/create_scripted_field.ts +++ b/src/plugins/data_views/server/rest_api_routes/scripted_fields/create_scripted_field.ts @@ -42,8 +42,9 @@ export const registerCreateScriptedFieldRoute = ( }, router.handleLegacyErrors( handleErrors(async (ctx, req, res) => { - const savedObjectsClient = ctx.core.savedObjects.client; - const elasticsearchClient = ctx.core.elasticsearch.client.asCurrentUser; + const core = await ctx.core; + const savedObjectsClient = core.savedObjects.client; + const elasticsearchClient = core.elasticsearch.client.asCurrentUser; const [, , { indexPatternsServiceFactory }] = await getStartServices(); const indexPatternsService = await indexPatternsServiceFactory( savedObjectsClient, diff --git a/src/plugins/data_views/server/rest_api_routes/scripted_fields/delete_scripted_field.ts b/src/plugins/data_views/server/rest_api_routes/scripted_fields/delete_scripted_field.ts index 811c3e096ca1..7e3333820e4e 100644 --- a/src/plugins/data_views/server/rest_api_routes/scripted_fields/delete_scripted_field.ts +++ b/src/plugins/data_views/server/rest_api_routes/scripted_fields/delete_scripted_field.ts @@ -43,8 +43,9 @@ export const registerDeleteScriptedFieldRoute = ( }, router.handleLegacyErrors( handleErrors(async (ctx, req, res) => { - const savedObjectsClient = ctx.core.savedObjects.client; - const elasticsearchClient = ctx.core.elasticsearch.client.asCurrentUser; + const core = await ctx.core; + const savedObjectsClient = core.savedObjects.client; + const elasticsearchClient = core.elasticsearch.client.asCurrentUser; const [, , { indexPatternsServiceFactory }] = await getStartServices(); const indexPatternsService = await indexPatternsServiceFactory( savedObjectsClient, diff --git a/src/plugins/data_views/server/rest_api_routes/scripted_fields/get_scripted_field.ts b/src/plugins/data_views/server/rest_api_routes/scripted_fields/get_scripted_field.ts index 3b76d56070f1..befe30f8437f 100644 --- a/src/plugins/data_views/server/rest_api_routes/scripted_fields/get_scripted_field.ts +++ b/src/plugins/data_views/server/rest_api_routes/scripted_fields/get_scripted_field.ts @@ -43,8 +43,9 @@ export const registerGetScriptedFieldRoute = ( }, router.handleLegacyErrors( handleErrors(async (ctx, req, res) => { - const savedObjectsClient = ctx.core.savedObjects.client; - const elasticsearchClient = ctx.core.elasticsearch.client.asCurrentUser; + const core = await ctx.core; + const savedObjectsClient = core.savedObjects.client; + const elasticsearchClient = core.elasticsearch.client.asCurrentUser; const [, , { indexPatternsServiceFactory }] = await getStartServices(); const indexPatternsService = await indexPatternsServiceFactory( savedObjectsClient, diff --git a/src/plugins/data_views/server/rest_api_routes/scripted_fields/put_scripted_field.ts b/src/plugins/data_views/server/rest_api_routes/scripted_fields/put_scripted_field.ts index 8989c1412769..93312dd6d3cf 100644 --- a/src/plugins/data_views/server/rest_api_routes/scripted_fields/put_scripted_field.ts +++ b/src/plugins/data_views/server/rest_api_routes/scripted_fields/put_scripted_field.ts @@ -42,8 +42,9 @@ export const registerPutScriptedFieldRoute = ( }, router.handleLegacyErrors( handleErrors(async (ctx, req, res) => { - const savedObjectsClient = ctx.core.savedObjects.client; - const elasticsearchClient = ctx.core.elasticsearch.client.asCurrentUser; + const core = await ctx.core; + const savedObjectsClient = core.savedObjects.client; + const elasticsearchClient = core.elasticsearch.client.asCurrentUser; const [, , { indexPatternsServiceFactory }] = await getStartServices(); const indexPatternsService = await indexPatternsServiceFactory( savedObjectsClient, diff --git a/src/plugins/data_views/server/rest_api_routes/scripted_fields/update_scripted_field.ts b/src/plugins/data_views/server/rest_api_routes/scripted_fields/update_scripted_field.ts index 28f28064b2a1..ddc9d7ae552e 100644 --- a/src/plugins/data_views/server/rest_api_routes/scripted_fields/update_scripted_field.ts +++ b/src/plugins/data_views/server/rest_api_routes/scripted_fields/update_scripted_field.ts @@ -63,8 +63,9 @@ export const registerUpdateScriptedFieldRoute = ( }, router.handleLegacyErrors( handleErrors(async (ctx, req, res) => { - const savedObjectsClient = ctx.core.savedObjects.client; - const elasticsearchClient = ctx.core.elasticsearch.client.asCurrentUser; + const core = await ctx.core; + const savedObjectsClient = core.savedObjects.client; + const elasticsearchClient = core.elasticsearch.client.asCurrentUser; const [, , { indexPatternsServiceFactory }] = await getStartServices(); const indexPatternsService = await indexPatternsServiceFactory( savedObjectsClient, diff --git a/src/plugins/data_views/server/rest_api_routes/update_data_view.ts b/src/plugins/data_views/server/rest_api_routes/update_data_view.ts index c0f890fda9d8..424680f85b49 100644 --- a/src/plugins/data_views/server/rest_api_routes/update_data_view.ts +++ b/src/plugins/data_views/server/rest_api_routes/update_data_view.ts @@ -161,8 +161,9 @@ const updateDataViewRouteFactory = }, router.handleLegacyErrors( handleErrors(async (ctx, req, res) => { - const savedObjectsClient = ctx.core.savedObjects.client; - const elasticsearchClient = ctx.core.elasticsearch.client.asCurrentUser; + const core = await ctx.core; + const savedObjectsClient = core.savedObjects.client; + const elasticsearchClient = core.elasticsearch.client.asCurrentUser; const [, , { dataViewsServiceFactory }] = await getStartServices(); const dataViewsService = await dataViewsServiceFactory( diff --git a/src/plugins/data_views/server/routes.ts b/src/plugins/data_views/server/routes.ts index 1a35b75b2a10..8f6299a58870 100644 --- a/src/plugins/data_views/server/routes.ts +++ b/src/plugins/data_views/server/routes.ts @@ -56,7 +56,7 @@ export function registerRoutes( }, }, async (context, request, response) => { - const { asCurrentUser } = context.core.elasticsearch.client; + const { asCurrentUser } = (await context.core).elasticsearch.client; const indexPatterns = new IndexPatternsFetcher(asCurrentUser); const { pattern, interval, look_back: lookBack, meta_fields: metaFields } = request.query; diff --git a/src/plugins/data_views/server/routes/fields_for.ts b/src/plugins/data_views/server/routes/fields_for.ts index cc71eb9258d4..3ad854ab0d6a 100644 --- a/src/plugins/data_views/server/routes/fields_for.ts +++ b/src/plugins/data_views/server/routes/fields_for.ts @@ -51,7 +51,7 @@ const validate: RouteValidatorFullConfig<{}, IQuery, IBody> = { body: schema.maybe(schema.object({ index_filter: schema.any() })), }; const handler: RequestHandler<{}, IQuery, IBody> = async (context, request, response) => { - const { asCurrentUser } = context.core.elasticsearch.client; + const { asCurrentUser } = (await context.core).elasticsearch.client; const indexPatterns = new IndexPatternsFetcher(asCurrentUser); const { pattern, diff --git a/src/plugins/data_views/server/routes/has_data_views.ts b/src/plugins/data_views/server/routes/has_data_views.ts index d74711e8c0d2..c4982311ce15 100644 --- a/src/plugins/data_views/server/routes/has_data_views.ts +++ b/src/plugins/data_views/server/routes/has_data_views.ts @@ -16,8 +16,9 @@ export const registerHasDataViewsRoute = (router: IRouter): void => { validate: {}, }, async (ctx, req, res) => { - const savedObjectsClient = ctx.core.savedObjects.client; - const elasticsearchClient = ctx.core.elasticsearch.client.asCurrentUser; + const core = await ctx.core; + const savedObjectsClient = core.savedObjects.client; + const elasticsearchClient = core.elasticsearch.client.asCurrentUser; const dataViews = await getIndexPattern({ esClient: elasticsearchClient, soClient: savedObjectsClient, diff --git a/src/plugins/home/server/routes/fetch_es_hits_status.ts b/src/plugins/home/server/routes/fetch_es_hits_status.ts index 557f93f0425e..0ce090c6bfee 100644 --- a/src/plugins/home/server/routes/fetch_es_hits_status.ts +++ b/src/plugins/home/server/routes/fetch_es_hits_status.ts @@ -22,7 +22,7 @@ export const registerHitsStatusRoute = (router: IRouter) => { }, router.handleLegacyErrors(async (context, req, res) => { const { index, query } = req.body; - const client = context.core.elasticsearch.client; + const client = (await context.core).elasticsearch.client; try { const body = await client.asCurrentUser.search({ diff --git a/src/plugins/home/server/services/sample_data/routes/install.ts b/src/plugins/home/server/services/sample_data/routes/install.ts index 58604ec30ce0..f057e6861518 100644 --- a/src/plugins/home/server/services/sample_data/routes/install.ts +++ b/src/plugins/home/server/services/sample_data/routes/install.ts @@ -38,7 +38,7 @@ export function createInstallRoute( // @ts-ignore Custom query validation used const now = query.now ? new Date(query.now) : new Date(); - const sampleDataInstaller = getSampleDataInstaller({ + const sampleDataInstaller = await getSampleDataInstaller({ datasetId: sampleDataset.id, sampleDatasets, logger, diff --git a/src/plugins/home/server/services/sample_data/routes/list.ts b/src/plugins/home/server/services/sample_data/routes/list.ts index 8f500b1c9550..39690b3944d0 100644 --- a/src/plugins/home/server/services/sample_data/routes/list.ts +++ b/src/plugins/home/server/services/sample_data/routes/list.ts @@ -78,7 +78,7 @@ async function findExistingSampleObjects( .map(({ savedObjects }) => savedObjects.map(({ type, id }) => ({ type, id }))) .flat(); const objectTypes = getUniqueObjectTypes(objects); - const client = getSavedObjectsClient(context, objectTypes); + const client = await getSavedObjectsClient(context, objectTypes); const findSampleObjectsResult = await findSampleObjects({ client, logger, objects }); let objectCounter = 0; @@ -101,18 +101,20 @@ async function getSampleDatasetStatus( return { status: NOT_INSTALLED }; } + const { elasticsearch } = await context.core; + for (let i = 0; i < sampleDataset.dataIndices.length; i++) { const dataIndexConfig = sampleDataset.dataIndices[i]; const index = createIndexName(sampleDataset.id, dataIndexConfig.id); try { - const indexExists = await context.core.elasticsearch.client.asCurrentUser.indices.exists({ + const indexExists = await elasticsearch.client.asCurrentUser.indices.exists({ index, }); if (!indexExists) { return { status: NOT_INSTALLED }; } - const count = await context.core.elasticsearch.client.asCurrentUser.count({ + const count = await elasticsearch.client.asCurrentUser.count({ index, }); if (count.count === 0) { diff --git a/src/plugins/home/server/services/sample_data/routes/uninstall.ts b/src/plugins/home/server/services/sample_data/routes/uninstall.ts index e6f4b9a07514..d2700b4985a7 100644 --- a/src/plugins/home/server/services/sample_data/routes/uninstall.ts +++ b/src/plugins/home/server/services/sample_data/routes/uninstall.ts @@ -32,7 +32,7 @@ export function createUninstallRoute( return response.notFound(); } - const sampleDataInstaller = getSampleDataInstaller({ + const sampleDataInstaller = await getSampleDataInstaller({ datasetId: sampleDataset.id, sampleDatasets, logger, diff --git a/src/plugins/home/server/services/sample_data/routes/utils.ts b/src/plugins/home/server/services/sample_data/routes/utils.ts index b784c50ff118..f4e99e4c04c9 100644 --- a/src/plugins/home/server/services/sample_data/routes/utils.ts +++ b/src/plugins/home/server/services/sample_data/routes/utils.ts @@ -11,7 +11,7 @@ import type { SampleDatasetSchema } from '../lib/sample_dataset_registry_types'; import { SampleDataInstaller } from '../sample_data_installer'; import { getUniqueObjectTypes } from '../lib/utils'; -export const getSampleDataInstaller = ({ +export const getSampleDataInstaller = async ({ datasetId, context, sampleDatasets, @@ -22,14 +22,15 @@ export const getSampleDataInstaller = ({ sampleDatasets: SampleDatasetSchema[]; logger: Logger; }) => { + const core = await context.core; const sampleDataset = sampleDatasets.find(({ id }) => id === datasetId)!; - const { getImporter, client: soClient } = context.core.savedObjects; + const { getImporter, client: soClient } = core.savedObjects; const objectTypes = getUniqueObjectTypes(sampleDataset.savedObjects); - const savedObjectsClient = getSavedObjectsClient(context, objectTypes); + const savedObjectsClient = await getSavedObjectsClient(context, objectTypes); const soImporter = getImporter(savedObjectsClient); return new SampleDataInstaller({ - esClient: context.core.elasticsearch.client, + esClient: core.elasticsearch.client, soImporter, soClient, logger, @@ -37,8 +38,11 @@ export const getSampleDataInstaller = ({ }); }; -export const getSavedObjectsClient = (context: RequestHandlerContext, objectTypes: string[]) => { - const { getClient, typeRegistry } = context.core.savedObjects; +export const getSavedObjectsClient = async ( + context: RequestHandlerContext, + objectTypes: string[] +) => { + const { getClient, typeRegistry } = (await context.core).savedObjects; const includedHiddenTypes = objectTypes.filter((supportedType) => typeRegistry.isHidden(supportedType) ); diff --git a/src/plugins/input_control_vis/public/plugin.ts b/src/plugins/input_control_vis/public/plugin.ts index 73158e17882f..f1f3cf609d72 100644 --- a/src/plugins/input_control_vis/public/plugin.ts +++ b/src/plugins/input_control_vis/public/plugin.ts @@ -9,7 +9,10 @@ import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from '@kbn/core/public'; import { DataPublicPluginSetup, DataPublicPluginStart } from '@kbn/data-plugin/public'; -import { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; +import { + UnifiedSearchPublicPluginStart, + UnifiedSearchPluginSetup, +} from '@kbn/unified-search-plugin/public'; import { Plugin as ExpressionsPublicPlugin } from '@kbn/expressions-plugin/public'; import { VisualizationsSetup, VisualizationsStart } from '@kbn/visualizations-plugin/public'; import { createInputControlVisFn } from './input_control_fn'; @@ -26,6 +29,7 @@ export interface InputControlSettings { export interface InputControlVisDependencies { core: InputControlVisCoreSetup; data: DataPublicPluginSetup; + unifiedSearch: UnifiedSearchPluginSetup; getSettings: () => Promise; } @@ -34,6 +38,7 @@ export interface InputControlVisPluginSetupDependencies { expressions: ReturnType; visualizations: VisualizationsSetup; data: DataPublicPluginSetup; + unifiedSearch: UnifiedSearchPluginSetup; } /** @internal */ @@ -50,15 +55,16 @@ export class InputControlVisPlugin implements Plugin { public setup( core: InputControlVisCoreSetup, - { expressions, visualizations, data }: InputControlVisPluginSetupDependencies + { expressions, visualizations, unifiedSearch, data }: InputControlVisPluginSetupDependencies ) { const visualizationDependencies: Readonly = { core, - data, + unifiedSearch, getSettings: async () => { - const { timeout, terminateAfter } = data.autocomplete.getAutocompleteSettings(); + const { timeout, terminateAfter } = unifiedSearch.autocomplete.getAutocompleteSettings(); return { autocompleteTimeout: timeout, autocompleteTerminateAfter: terminateAfter }; }, + data, }; expressions.registerFunction(createInputControlVisFn); diff --git a/src/plugins/saved_objects_management/server/routes/bulk_get.ts b/src/plugins/saved_objects_management/server/routes/bulk_get.ts index fab25b51d922..9e31b1c24b0b 100644 --- a/src/plugins/saved_objects_management/server/routes/bulk_get.ts +++ b/src/plugins/saved_objects_management/server/routes/bulk_get.ts @@ -29,7 +29,7 @@ export const registerBulkGetRoute = ( }, router.handleLegacyErrors(async (context, req, res) => { const managementService = await managementServicePromise; - const { getClient, typeRegistry } = context.core.savedObjects; + const { getClient, typeRegistry } = (await context.core).savedObjects; const objects = req.body; const uniqueTypes = objects.reduce((acc, { type }) => acc.add(type), new Set()); diff --git a/src/plugins/saved_objects_management/server/routes/find.ts b/src/plugins/saved_objects_management/server/routes/find.ts index 662ab6803115..05e69da5d320 100644 --- a/src/plugins/saved_objects_management/server/routes/find.ts +++ b/src/plugins/saved_objects_management/server/routes/find.ts @@ -47,7 +47,7 @@ export const registerFindRoute = ( router.handleLegacyErrors(async (context, req, res) => { const { query } = req; const managementService = await managementServicePromise; - const { getClient, typeRegistry } = context.core.savedObjects; + const { getClient, typeRegistry } = (await context.core).savedObjects; const searchTypes = Array.isArray(query.type) ? query.type : [query.type]; const includedFields = Array.isArray(query.fields) ? query.fields : [query.fields]; diff --git a/src/plugins/saved_objects_management/server/routes/get_allowed_types.ts b/src/plugins/saved_objects_management/server/routes/get_allowed_types.ts index 3ad8130654a7..cdd6dc215d69 100644 --- a/src/plugins/saved_objects_management/server/routes/get_allowed_types.ts +++ b/src/plugins/saved_objects_management/server/routes/get_allowed_types.ts @@ -25,7 +25,7 @@ export const registerGetAllowedTypesRoute = (router: IRouter) => { validate: false, }, async (context, req, res) => { - const allowedTypes = context.core.savedObjects.typeRegistry + const allowedTypes = (await context.core).savedObjects.typeRegistry .getImportableAndExportableTypes() .filter((type) => type.management!.visibleInManagement ?? true) .map(convertType); diff --git a/src/plugins/saved_objects_management/server/routes/relationships.ts b/src/plugins/saved_objects_management/server/routes/relationships.ts index 875cf82488ab..8900987a645f 100644 --- a/src/plugins/saved_objects_management/server/routes/relationships.ts +++ b/src/plugins/saved_objects_management/server/routes/relationships.ts @@ -32,7 +32,7 @@ export const registerRelationshipsRoute = ( }, router.handleLegacyErrors(async (context, req, res) => { const managementService = await managementServicePromise; - const { getClient, typeRegistry } = context.core.savedObjects; + const { getClient, typeRegistry } = (await context.core).savedObjects; const { type, id } = req.params; const { size, savedObjectTypes: maybeArraySavedObjectTypes } = req.query; const savedObjectTypes = Array.isArray(maybeArraySavedObjectTypes) diff --git a/src/plugins/saved_objects_management/server/routes/scroll_count.ts b/src/plugins/saved_objects_management/server/routes/scroll_count.ts index 217153bbb86e..26dd1d57b4cf 100644 --- a/src/plugins/saved_objects_management/server/routes/scroll_count.ts +++ b/src/plugins/saved_objects_management/server/routes/scroll_count.ts @@ -31,7 +31,7 @@ export const registerScrollForCountRoute = (router: IRouter) => { }, }, router.handleLegacyErrors(async (context, req, res) => { - const { getClient, typeRegistry } = context.core.savedObjects; + const { getClient, typeRegistry } = (await context.core).savedObjects; const { typesToInclude, searchString, references } = req.body; const includedHiddenTypes = chain(typesToInclude) diff --git a/src/plugins/screenshot_mode/server/types.ts b/src/plugins/screenshot_mode/server/types.ts index 514bcb759428..08655d2f0118 100644 --- a/src/plugins/screenshot_mode/server/types.ts +++ b/src/plugins/screenshot_mode/server/types.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import type { RequestHandlerContext, KibanaRequest } from '@kbn/core/server'; +import type { CustomRequestHandlerContext, KibanaRequest } from '@kbn/core/server'; export interface ScreenshotModePluginStart { /** @@ -32,8 +32,8 @@ export interface ScreenshotModePluginSetup extends ScreenshotModePluginStart { setScreenshotModeEnabled(): void; } -export interface ScreenshotModeRequestHandlerContext extends RequestHandlerContext { +export type ScreenshotModeRequestHandlerContext = CustomRequestHandlerContext<{ screenshotMode: { isScreenshot: boolean; }; -} +}>; diff --git a/src/plugins/share/server/url_service/http/short_urls/register_create_route.ts b/src/plugins/share/server/url_service/http/short_urls/register_create_route.ts index 972641d160e2..1208f6fda4d1 100644 --- a/src/plugins/share/server/url_service/http/short_urls/register_create_route.ts +++ b/src/plugins/share/server/url_service/http/short_urls/register_create_route.ts @@ -34,7 +34,7 @@ export const registerCreateRoute = (router: IRouter, url: ServerUrlService) => { }, }, router.handleLegacyErrors(async (ctx, req, res) => { - const savedObjects = ctx.core.savedObjects.client; + const savedObjects = (await ctx.core).savedObjects.client; const shortUrls = url.shortUrls.get({ savedObjects }); const { locatorId, params, slug, humanReadableSlug } = req.body; const locator = url.locators.get(locatorId); diff --git a/src/plugins/share/server/url_service/http/short_urls/register_delete_route.ts b/src/plugins/share/server/url_service/http/short_urls/register_delete_route.ts index 6e2b133ac3cf..ddc29117a3ac 100644 --- a/src/plugins/share/server/url_service/http/short_urls/register_delete_route.ts +++ b/src/plugins/share/server/url_service/http/short_urls/register_delete_route.ts @@ -25,7 +25,7 @@ export const registerDeleteRoute = (router: IRouter, url: ServerUrlService) => { }, router.handleLegacyErrors(async (ctx, req, res) => { const id = req.params.id; - const savedObjects = ctx.core.savedObjects.client; + const savedObjects = (await ctx.core).savedObjects.client; const shortUrls = url.shortUrls.get({ savedObjects }); await shortUrls.delete(id); diff --git a/src/plugins/share/server/url_service/http/short_urls/register_get_route.ts b/src/plugins/share/server/url_service/http/short_urls/register_get_route.ts index 7ece9b1ef6c6..8e783b3fcfd3 100644 --- a/src/plugins/share/server/url_service/http/short_urls/register_get_route.ts +++ b/src/plugins/share/server/url_service/http/short_urls/register_get_route.ts @@ -25,7 +25,7 @@ export const registerGetRoute = (router: IRouter, url: ServerUrlService) => { }, router.handleLegacyErrors(async (ctx, req, res) => { const id = req.params.id; - const savedObjects = ctx.core.savedObjects.client; + const savedObjects = (await ctx.core).savedObjects.client; const shortUrls = url.shortUrls.get({ savedObjects }); const shortUrl = await shortUrls.get(id); diff --git a/src/plugins/share/server/url_service/http/short_urls/register_resolve_route.ts b/src/plugins/share/server/url_service/http/short_urls/register_resolve_route.ts index b4c73780bdf7..47290d575454 100644 --- a/src/plugins/share/server/url_service/http/short_urls/register_resolve_route.ts +++ b/src/plugins/share/server/url_service/http/short_urls/register_resolve_route.ts @@ -26,7 +26,7 @@ export const registerResolveRoute = (router: IRouter, url: ServerUrlService) => }, router.handleLegacyErrors(async (ctx, req, res) => { const slug = req.params.slug; - const savedObjects = ctx.core.savedObjects.client; + const savedObjects = (await ctx.core).savedObjects.client; try { const shortUrls = url.shortUrls.get({ savedObjects }); diff --git a/src/plugins/telemetry/server/routes/telemetry_opt_in.ts b/src/plugins/telemetry/server/routes/telemetry_opt_in.ts index fff5df2dff10..06e443670518 100644 --- a/src/plugins/telemetry/server/routes/telemetry_opt_in.ts +++ b/src/plugins/telemetry/server/routes/telemetry_opt_in.ts @@ -48,12 +48,13 @@ export function registerTelemetryOptInRoutes({ }, async (context, req, res) => { const newOptInStatus = req.body.enabled; + const soClient = (await context.core).savedObjects.client; const attributes: TelemetrySavedObjectAttributes = { enabled: newOptInStatus, lastVersionChecked: currentKibanaVersion, }; const config = await firstValueFrom(config$); - const telemetrySavedObject = await getTelemetrySavedObject(context.core.savedObjects.client); + const telemetrySavedObject = await getTelemetrySavedObject(soClient); if (telemetrySavedObject === false) { // If we get false, we couldn't get the saved object due to lack of permissions @@ -96,7 +97,7 @@ export function registerTelemetryOptInRoutes({ } try { - await updateTelemetrySavedObject(context.core.savedObjects.client, attributes); + await updateTelemetrySavedObject(soClient, attributes); } catch (e) { if (SavedObjectsErrorHelpers.isForbiddenError(e)) { return res.forbidden(); diff --git a/src/plugins/telemetry/server/routes/telemetry_user_has_seen_notice.ts b/src/plugins/telemetry/server/routes/telemetry_user_has_seen_notice.ts index 686274dc4f5d..cb990b68fd9b 100644 --- a/src/plugins/telemetry/server/routes/telemetry_user_has_seen_notice.ts +++ b/src/plugins/telemetry/server/routes/telemetry_user_has_seen_notice.ts @@ -21,7 +21,7 @@ export function registerTelemetryUserHasSeenNotice(router: IRouter) { validate: false, }, async (context, req, res) => { - const internalRepository = context.core.savedObjects.client; + const internalRepository = (await context.core).savedObjects.client; const telemetrySavedObject: TelemetrySavedObject = await getTelemetrySavedObject( internalRepository ); diff --git a/src/plugins/unified_search/config.ts b/src/plugins/unified_search/config.ts index c0067771b680..4814dddea405 100644 --- a/src/plugins/unified_search/config.ts +++ b/src/plugins/unified_search/config.ts @@ -8,6 +8,29 @@ import { schema, TypeOf } from '@kbn/config-schema'; -export const configSchema = schema.object({}); +export const configSchema = schema.object({ + autocomplete: schema.object({ + querySuggestions: schema.object({ + enabled: schema.boolean({ defaultValue: true }), + }), + valueSuggestions: schema.object({ + enabled: schema.boolean({ defaultValue: true }), + tiers: schema.arrayOf( + schema.oneOf([ + schema.literal('data_content'), + schema.literal('data_hot'), + schema.literal('data_warm'), + schema.literal('data_cold'), + schema.literal('data_frozen'), + ]), + { + defaultValue: ['data_hot', 'data_warm', 'data_content', 'data_cold'], + } + ), + terminateAfter: schema.duration({ defaultValue: 100000 }), + timeout: schema.duration({ defaultValue: 1000 }), + }), + }), +}); export type ConfigSchema = TypeOf; diff --git a/src/plugins/unified_search/kibana.json b/src/plugins/unified_search/kibana.json index 8673c6ec783b..b947141a0c68 100755 --- a/src/plugins/unified_search/kibana.json +++ b/src/plugins/unified_search/kibana.json @@ -7,8 +7,10 @@ "name": "Unified Search", "githubTeam": "kibana-app-services" }, - "server": false, + "server": true, "ui": true, "requiredPlugins": ["dataViews", "data", "uiActions"], - "requiredBundles": ["kibanaUtils", "kibanaReact", "data"] + "requiredBundles": ["kibanaUtils", "kibanaReact", "data"], + "serviceFolders": ["autocomplete"], + "configPath": ["unifiedSearch"] } diff --git a/src/plugins/data/public/autocomplete/autocomplete_service.ts b/src/plugins/unified_search/public/autocomplete/autocomplete_service.ts similarity index 93% rename from src/plugins/data/public/autocomplete/autocomplete_service.ts rename to src/plugins/unified_search/public/autocomplete/autocomplete_service.ts index 95db8e31eb4d..97d128df5a78 100644 --- a/src/plugins/data/public/autocomplete/autocomplete_service.ts +++ b/src/plugins/unified_search/public/autocomplete/autocomplete_service.ts @@ -8,8 +8,8 @@ import { CoreSetup, PluginInitializerContext } from '@kbn/core/public'; import moment from 'moment'; +import type { TimefilterSetup } from '@kbn/data-plugin/public'; import { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public'; -import type { TimefilterSetup } from '../query'; import { QuerySuggestionGetFn } from './providers/query_suggestion_provider'; import { getEmptyValueSuggestions, @@ -23,7 +23,7 @@ import { KUERY_LANGUAGE_NAME, setupKqlQuerySuggestionProvider, } from './providers/kql_query_suggestion'; -import { DataPublicPluginStart, DataStartDependencies } from '../types'; +import { UnifiedSearchPublicPluginStart, UnifiedSearchStartDependencies } from '../types'; export class AutocompleteService { autocompleteConfig: ConfigSchema['autocomplete']; @@ -50,7 +50,7 @@ export class AutocompleteService { /** @public **/ public setup( - core: CoreSetup, + core: CoreSetup, { timefilter, usageCollection, diff --git a/src/plugins/data/public/autocomplete/collectors/create_usage_collector.ts b/src/plugins/unified_search/public/autocomplete/collectors/create_usage_collector.ts similarity index 100% rename from src/plugins/data/public/autocomplete/collectors/create_usage_collector.ts rename to src/plugins/unified_search/public/autocomplete/collectors/create_usage_collector.ts diff --git a/src/plugins/data/public/autocomplete/collectors/index.ts b/src/plugins/unified_search/public/autocomplete/collectors/index.ts similarity index 100% rename from src/plugins/data/public/autocomplete/collectors/index.ts rename to src/plugins/unified_search/public/autocomplete/collectors/index.ts diff --git a/src/plugins/data/public/autocomplete/collectors/types.ts b/src/plugins/unified_search/public/autocomplete/collectors/types.ts similarity index 100% rename from src/plugins/data/public/autocomplete/collectors/types.ts rename to src/plugins/unified_search/public/autocomplete/collectors/types.ts diff --git a/src/plugins/data/public/autocomplete/index.ts b/src/plugins/unified_search/public/autocomplete/index.ts similarity index 100% rename from src/plugins/data/public/autocomplete/index.ts rename to src/plugins/unified_search/public/autocomplete/index.ts diff --git a/src/plugins/data/public/autocomplete/providers/kql_query_suggestion/README.md b/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/README.md similarity index 100% rename from src/plugins/data/public/autocomplete/providers/kql_query_suggestion/README.md rename to src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/README.md diff --git a/src/plugins/data/public/autocomplete/providers/kql_query_suggestion/__fixtures__/index_pattern_response.json b/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/__fixtures__/index_pattern_response.json similarity index 100% rename from src/plugins/data/public/autocomplete/providers/kql_query_suggestion/__fixtures__/index_pattern_response.json rename to src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/__fixtures__/index_pattern_response.json diff --git a/src/plugins/data/public/autocomplete/providers/kql_query_suggestion/conjunction.test.ts b/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/conjunction.test.ts similarity index 95% rename from src/plugins/data/public/autocomplete/providers/kql_query_suggestion/conjunction.test.ts rename to src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/conjunction.test.ts index 68b0d123f0a9..24a27bcb99fb 100644 --- a/src/plugins/data/public/autocomplete/providers/kql_query_suggestion/conjunction.test.ts +++ b/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/conjunction.test.ts @@ -6,9 +6,10 @@ * Side Public License, v 1. */ -import { setupGetConjunctionSuggestions } from './conjunction'; -import { QuerySuggestionGetFnArgs, KueryNode } from '../../..'; import { coreMock } from '@kbn/core/public/mocks'; +import { KueryNode } from '@kbn/data-plugin/public'; +import { setupGetConjunctionSuggestions } from './conjunction'; +import { QuerySuggestionGetFnArgs } from '../query_suggestion_provider'; const mockKueryNode = (kueryNode: Partial) => kueryNode as unknown as KueryNode; diff --git a/src/plugins/data/public/autocomplete/providers/kql_query_suggestion/conjunction.tsx b/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/conjunction.tsx similarity index 69% rename from src/plugins/data/public/autocomplete/providers/kql_query_suggestion/conjunction.tsx rename to src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/conjunction.tsx index c11c54c2a6f3..ad757f5afbf9 100644 --- a/src/plugins/data/public/autocomplete/providers/kql_query_suggestion/conjunction.tsx +++ b/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/conjunction.tsx @@ -10,21 +10,21 @@ import React from 'react'; import { $Keys } from 'utility-types'; import { FormattedMessage } from '@kbn/i18n-react'; import { KqlQuerySuggestionProvider } from './types'; -import { QuerySuggestion, QuerySuggestionTypes } from '../../..'; +import { QuerySuggestion, QuerySuggestionTypes } from '../query_suggestion_provider'; const bothArgumentsText = ( ); const oneOrMoreArgumentsText = ( ); @@ -32,20 +32,20 @@ const conjunctions: Record = { and: (

{bothArgumentsText}, }} description="Full text: ' Requires both arguments to be true'. See - 'data.kueryAutocomplete.andOperatorDescription.bothArgumentsText' for 'both arguments' part." + 'unifiedSearch.kueryAutocomplete.andOperatorDescription.bothArgumentsText' for 'both arguments' part." />

), or: (

= { ), }} description="Full text: 'Requires one or more arguments to be true'. See - 'data.kueryAutocomplete.orOperatorDescription.oneOrMoreArgumentsText' for 'one or more arguments' part." + 'unifiedSearch.kueryAutocomplete.orOperatorDescription.oneOrMoreArgumentsText' for 'one or more arguments' part." />

), diff --git a/src/plugins/data/public/autocomplete/providers/kql_query_suggestion/field.test.ts b/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/field.test.ts similarity index 97% rename from src/plugins/data/public/autocomplete/providers/kql_query_suggestion/field.test.ts rename to src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/field.test.ts index 7e1633a2cd5e..4446fcf685bd 100644 --- a/src/plugins/data/public/autocomplete/providers/kql_query_suggestion/field.test.ts +++ b/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/field.test.ts @@ -8,8 +8,9 @@ import indexPatternResponse from './__fixtures__/index_pattern_response.json'; +import { indexPatterns as indexPatternsUtils, KueryNode } from '@kbn/data-plugin/public'; import { setupGetFieldSuggestions } from './field'; -import { indexPatterns as indexPatternsUtils, QuerySuggestionGetFnArgs, KueryNode } from '../../..'; +import { QuerySuggestionGetFnArgs } from '../query_suggestion_provider'; import { coreMock } from '@kbn/core/public/mocks'; const mockKueryNode = (kueryNode: Partial) => kueryNode as unknown as KueryNode; diff --git a/src/plugins/data/public/autocomplete/providers/kql_query_suggestion/field.tsx b/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/field.tsx similarity index 93% rename from src/plugins/data/public/autocomplete/providers/kql_query_suggestion/field.tsx rename to src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/field.tsx index 9664353515c6..139405f6af9f 100644 --- a/src/plugins/data/public/autocomplete/providers/kql_query_suggestion/field.tsx +++ b/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/field.tsx @@ -6,15 +6,11 @@ * Side Public License, v 1. */ +import { IFieldType, indexPatterns as indexPatternsUtils } from '@kbn/data-plugin/public'; import { flatten } from 'lodash'; import { escapeKuery } from './lib/escape_kuery'; import { sortPrefixFirst } from './sort_prefix_first'; -import { - IFieldType, - indexPatterns as indexPatternsUtils, - QuerySuggestionField, - QuerySuggestionTypes, -} from '../../..'; +import { QuerySuggestionField, QuerySuggestionTypes } from '../query_suggestion_provider'; import { KqlQuerySuggestionProvider } from './types'; const keywordComparator = (first: IFieldType, second: IFieldType) => { diff --git a/src/plugins/data/public/autocomplete/providers/kql_query_suggestion/index.ts b/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/index.ts similarity index 93% rename from src/plugins/data/public/autocomplete/providers/kql_query_suggestion/index.ts rename to src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/index.ts index 6ddf518933af..1002863fec7f 100644 --- a/src/plugins/data/public/autocomplete/providers/kql_query_suggestion/index.ts +++ b/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/index.ts @@ -14,12 +14,12 @@ import { setupGetFieldSuggestions } from './field'; import { setupGetValueSuggestions } from './value'; import { setupGetOperatorSuggestions } from './operator'; import { setupGetConjunctionSuggestions } from './conjunction'; +import { UnifiedSearchPublicPluginStart } from '../../../types'; import { QuerySuggestion, QuerySuggestionGetFnArgs, QuerySuggestionGetFn, - DataPublicPluginStart, -} from '../../..'; +} from '../query_suggestion_provider'; const cursorSymbol = '@kuery-cursor@'; @@ -29,7 +29,7 @@ const dedup = (suggestions: QuerySuggestion[]): QuerySuggestion[] => export const KUERY_LANGUAGE_NAME = 'kuery'; export const setupKqlQuerySuggestionProvider = ( - core: CoreSetup + core: CoreSetup ): QuerySuggestionGetFn => { const providers = { field: setupGetFieldSuggestions(core), diff --git a/src/plugins/data/public/autocomplete/providers/kql_query_suggestion/lib/escape_kuery.test.ts b/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/lib/escape_kuery.test.ts similarity index 100% rename from src/plugins/data/public/autocomplete/providers/kql_query_suggestion/lib/escape_kuery.test.ts rename to src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/lib/escape_kuery.test.ts diff --git a/src/plugins/data/public/autocomplete/providers/kql_query_suggestion/lib/escape_kuery.ts b/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/lib/escape_kuery.ts similarity index 100% rename from src/plugins/data/public/autocomplete/providers/kql_query_suggestion/lib/escape_kuery.ts rename to src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/lib/escape_kuery.ts diff --git a/src/plugins/data/public/autocomplete/providers/kql_query_suggestion/operator.test.ts b/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/operator.test.ts similarity index 97% rename from src/plugins/data/public/autocomplete/providers/kql_query_suggestion/operator.test.ts rename to src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/operator.test.ts index 6a2411cc5ccb..a40678ad4ac1 100644 --- a/src/plugins/data/public/autocomplete/providers/kql_query_suggestion/operator.test.ts +++ b/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/operator.test.ts @@ -9,7 +9,8 @@ import indexPatternResponse from './__fixtures__/index_pattern_response.json'; import { setupGetOperatorSuggestions } from './operator'; -import { QuerySuggestionGetFnArgs, KueryNode } from '../../..'; +import { KueryNode } from '@kbn/data-plugin/public'; +import { QuerySuggestionGetFnArgs } from '../query_suggestion_provider'; import { coreMock } from '@kbn/core/public/mocks'; const mockKueryNode = (kueryNode: Partial) => kueryNode as unknown as KueryNode; diff --git a/src/plugins/data/public/autocomplete/providers/kql_query_suggestion/operator.tsx b/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/operator.tsx similarity index 65% rename from src/plugins/data/public/autocomplete/providers/kql_query_suggestion/operator.tsx rename to src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/operator.tsx index f6517f67218d..ee53d5cb3d39 100644 --- a/src/plugins/data/public/autocomplete/providers/kql_query_suggestion/operator.tsx +++ b/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/operator.tsx @@ -12,48 +12,48 @@ import { $Keys } from 'utility-types'; import { flatten } from 'lodash'; import { KqlQuerySuggestionProvider } from './types'; -import { QuerySuggestionTypes } from '../../..'; +import { QuerySuggestionTypes } from '../query_suggestion_provider'; const equalsText = ( ); const lessThanOrEqualToText = ( ); const greaterThanOrEqualToText = ( ); const lessThanText = ( ); const greaterThanText = ( ); const existsText = ( ); @@ -61,11 +61,11 @@ const operators = { ':': { description: ( {equalsText} }} description="Full text: 'equals some value'. See - 'data.kueryAutocomplete.equalOperatorDescription.equalsText' for 'equals' part." + 'unifiedSearch.kueryAutocomplete.equalOperatorDescription.equalsText' for 'equals' part." /> ), fieldTypes: [ @@ -84,7 +84,7 @@ const operators = { '<=': { description: ( ), fieldTypes: ['number', 'number_range', 'date', 'date_range', 'ip', 'ip_range'], @@ -100,7 +100,7 @@ const operators = { '>=': { description: ( ), fieldTypes: ['number', 'number_range', 'date', 'date_range', 'ip', 'ip_range'], @@ -116,11 +116,11 @@ const operators = { '<': { description: ( {lessThanText} }} description="Full text: 'is less than some value'. See - 'data.kueryAutocomplete.lessThanOperatorDescription.lessThanText' for 'less than' part." + 'unifiedSearch.kueryAutocomplete.lessThanOperatorDescription.lessThanText' for 'less than' part." /> ), fieldTypes: ['number', 'number_range', 'date', 'date_range', 'ip', 'ip_range'], @@ -128,13 +128,13 @@ const operators = { '>': { description: ( {greaterThanText}, }} description="Full text: 'is greater than some value'. See - 'data.kueryAutocomplete.greaterThanOperatorDescription.greaterThanText' for 'greater than' part." + 'unifiedSearch.kueryAutocomplete.greaterThanOperatorDescription.greaterThanText' for 'greater than' part." /> ), fieldTypes: ['number', 'number_range', 'date', 'date_range', 'ip', 'ip_range'], @@ -142,11 +142,11 @@ const operators = { ': *': { description: ( {existsText} }} description="Full text: 'exists in any form'. See - 'data.kueryAutocomplete.existOperatorDescription.existsText' for 'exists' part." + 'unifiedSearch.kueryAutocomplete.existOperatorDescription.existsText' for 'exists' part." /> ), fieldTypes: undefined, diff --git a/src/plugins/data/public/autocomplete/providers/kql_query_suggestion/sort_prefix_first.test.ts b/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/sort_prefix_first.test.ts similarity index 100% rename from src/plugins/data/public/autocomplete/providers/kql_query_suggestion/sort_prefix_first.test.ts rename to src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/sort_prefix_first.test.ts diff --git a/src/plugins/data/public/autocomplete/providers/kql_query_suggestion/sort_prefix_first.ts b/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/sort_prefix_first.ts similarity index 100% rename from src/plugins/data/public/autocomplete/providers/kql_query_suggestion/sort_prefix_first.ts rename to src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/sort_prefix_first.ts diff --git a/src/plugins/data/public/autocomplete/providers/kql_query_suggestion/types.ts b/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/types.ts similarity index 73% rename from src/plugins/data/public/autocomplete/providers/kql_query_suggestion/types.ts rename to src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/types.ts index b39f942b1f76..e9ca34e546f0 100644 --- a/src/plugins/data/public/autocomplete/providers/kql_query_suggestion/types.ts +++ b/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/types.ts @@ -6,10 +6,11 @@ * Side Public License, v 1. */ -import { KueryNode } from '@kbn/es-query'; import { CoreSetup } from '@kbn/core/public'; -import { DataPublicPluginStart, QuerySuggestionBasic, QuerySuggestionGetFnArgs } from '../../..'; +import { KueryNode } from '@kbn/es-query'; +import type { UnifiedSearchPublicPluginStart } from '../../../types'; +import { QuerySuggestionBasic, QuerySuggestionGetFnArgs } from '../query_suggestion_provider'; export type KqlQuerySuggestionProvider = ( - core: CoreSetup + core: CoreSetup ) => (querySuggestionsGetFnArgs: QuerySuggestionGetFnArgs, kueryNode: KueryNode) => Promise; diff --git a/src/plugins/data/public/autocomplete/providers/kql_query_suggestion/value.test.ts b/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/value.test.ts similarity index 97% rename from src/plugins/data/public/autocomplete/providers/kql_query_suggestion/value.test.ts rename to src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/value.test.ts index 224865449a5a..3405d26824a2 100644 --- a/src/plugins/data/public/autocomplete/providers/kql_query_suggestion/value.test.ts +++ b/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/value.test.ts @@ -8,8 +8,10 @@ import { setupGetValueSuggestions } from './value'; import indexPatternResponse from './__fixtures__/index_pattern_response.json'; + import { coreMock } from '@kbn/core/public/mocks'; -import { QuerySuggestionGetFnArgs, KueryNode } from '../../..'; +import { KueryNode } from '@kbn/data-plugin/public'; +import { QuerySuggestionGetFnArgs } from '../query_suggestion_provider'; const mockKueryNode = (kueryNode: Partial) => kueryNode as unknown as KueryNode; diff --git a/src/plugins/data/public/autocomplete/providers/kql_query_suggestion/value.ts b/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/value.ts similarity index 89% rename from src/plugins/data/public/autocomplete/providers/kql_query_suggestion/value.ts rename to src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/value.ts index b75cf9d09d27..06b0fc9639a3 100644 --- a/src/plugins/data/public/autocomplete/providers/kql_query_suggestion/value.ts +++ b/src/plugins/unified_search/public/autocomplete/providers/kql_query_suggestion/value.ts @@ -8,15 +8,11 @@ import { flatten } from 'lodash'; import { CoreSetup } from '@kbn/core/public'; +import { IFieldType, IIndexPattern } from '@kbn/data-plugin/public'; import { escapeQuotes } from './lib/escape_kuery'; import { KqlQuerySuggestionProvider } from './types'; -import { - DataPublicPluginStart, - IFieldType, - IIndexPattern, - QuerySuggestion, - QuerySuggestionTypes, -} from '../../..'; +import type { UnifiedSearchPublicPluginStart } from '../../../types'; +import { QuerySuggestion, QuerySuggestionTypes } from '../query_suggestion_provider'; const wrapAsSuggestions = (start: number, end: number, query: string, values: string[]) => values @@ -29,7 +25,7 @@ const wrapAsSuggestions = (start: number, end: number, query: string, values: st })); export const setupGetValueSuggestions: KqlQuerySuggestionProvider = ( - core: CoreSetup + core: CoreSetup ) => { const autoCompleteServicePromise = core .getStartServices() diff --git a/src/plugins/data/public/autocomplete/providers/query_suggestion_provider.ts b/src/plugins/unified_search/public/autocomplete/providers/query_suggestion_provider.ts similarity index 90% rename from src/plugins/data/public/autocomplete/providers/query_suggestion_provider.ts rename to src/plugins/unified_search/public/autocomplete/providers/query_suggestion_provider.ts index 0cb229c9b41b..056fcb716054 100644 --- a/src/plugins/data/public/autocomplete/providers/query_suggestion_provider.ts +++ b/src/plugins/unified_search/public/autocomplete/providers/query_suggestion_provider.ts @@ -6,8 +6,8 @@ * Side Public License, v 1. */ -import { ValueSuggestionsMethod } from '../../../common'; -import { IFieldType, IIndexPattern } from '../../../common'; +import { ValueSuggestionsMethod } from '@kbn/data-plugin/common'; +import { IFieldType, IIndexPattern } from '@kbn/data-plugin/common'; export enum QuerySuggestionTypes { Field = 'field', diff --git a/src/plugins/data/public/autocomplete/providers/value_suggestion_provider.test.ts b/src/plugins/unified_search/public/autocomplete/providers/value_suggestion_provider.test.ts similarity index 97% rename from src/plugins/data/public/autocomplete/providers/value_suggestion_provider.test.ts rename to src/plugins/unified_search/public/autocomplete/providers/value_suggestion_provider.test.ts index bf80438da08a..a17172a2b607 100644 --- a/src/plugins/data/public/autocomplete/providers/value_suggestion_provider.test.ts +++ b/src/plugins/unified_search/public/autocomplete/providers/value_suggestion_provider.test.ts @@ -6,12 +6,12 @@ * Side Public License, v 1. */ -import { stubIndexPattern, stubFields } from '../../stubs'; -import type { TimefilterSetup } from '../../query'; +import { IUiSettingsClient, CoreSetup } from '@kbn/core/public'; +import { stubIndexPattern, stubFields } from '@kbn/data-plugin/public/stubs'; +import type { TimefilterSetup } from '@kbn/data-plugin/public'; +import { UI_SETTINGS } from '@kbn/data-plugin/common'; import { setupValueSuggestionProvider } from './value_suggestion_provider'; import type { ValueSuggestionsGetFn } from './value_suggestion_provider'; -import { IUiSettingsClient, CoreSetup } from '@kbn/core/public'; -import { UI_SETTINGS } from '../../../common'; describe('FieldSuggestions', () => { let getValueSuggestions: ValueSuggestionsGetFn; diff --git a/src/plugins/data/public/autocomplete/providers/value_suggestion_provider.ts b/src/plugins/unified_search/public/autocomplete/providers/value_suggestion_provider.ts similarity index 95% rename from src/plugins/data/public/autocomplete/providers/value_suggestion_provider.ts rename to src/plugins/unified_search/public/autocomplete/providers/value_suggestion_provider.ts index 422c4e02d8e4..054a24306432 100644 --- a/src/plugins/data/public/autocomplete/providers/value_suggestion_provider.ts +++ b/src/plugins/unified_search/public/autocomplete/providers/value_suggestion_provider.ts @@ -6,12 +6,17 @@ * Side Public License, v 1. */ +import { CoreSetup } from '@kbn/core/public'; import dateMath from '@kbn/datemath'; import { buildQueryFromFilters } from '@kbn/es-query'; import { memoize } from 'lodash'; -import { CoreSetup } from '@kbn/core/public'; -import { IIndexPattern, IFieldType, UI_SETTINGS, ValueSuggestionsMethod } from '../../../common'; -import type { TimefilterSetup } from '../../query'; +import { + IIndexPattern, + IFieldType, + UI_SETTINGS, + ValueSuggestionsMethod, +} from '@kbn/data-plugin/common'; +import type { TimefilterSetup } from '@kbn/data-plugin/public'; import { AutocompleteUsageCollector } from '../collectors'; export type ValueSuggestionsGetFn = (args: ValueSuggestionsGetFnArgs) => Promise; diff --git a/src/plugins/unified_search/public/filter_bar/filter_editor/phrase_suggestor.tsx b/src/plugins/unified_search/public/filter_bar/filter_editor/phrase_suggestor.tsx index 057a730e5cc4..50acadea2a99 100644 --- a/src/plugins/unified_search/public/filter_bar/filter_editor/phrase_suggestor.tsx +++ b/src/plugins/unified_search/public/filter_bar/filter_editor/phrase_suggestor.tsx @@ -7,12 +7,13 @@ */ import React from 'react'; -import { debounce } from 'lodash'; - import { withKibana, KibanaReactContextValue } from '@kbn/kibana-react-plugin/public'; import { IFieldType, UI_SETTINGS } from '@kbn/data-plugin/common'; import { DataView } from '@kbn/data-views-plugin/common'; import { IDataPluginServices } from '@kbn/data-plugin/public'; +import { debounce } from 'lodash'; + +import { getAutocomplete } from '../../services'; export interface PhraseSuggestorProps { kibana: KibanaReactContextValue; @@ -79,8 +80,7 @@ export class PhraseSuggestorUI extends React.Com return; } this.setState({ isLoading: true }); - - const suggestions = await this.services.data.autocomplete.getValueSuggestions({ + const suggestions = await getAutocomplete().getValueSuggestions({ indexPattern, field, query, diff --git a/src/plugins/unified_search/public/index.ts b/src/plugins/unified_search/public/index.ts index ff020ef1e8f9..1200234a793a 100755 --- a/src/plugins/unified_search/public/index.ts +++ b/src/plugins/unified_search/public/index.ts @@ -7,19 +7,31 @@ */ import { PluginInitializerContext } from '@kbn/core/public'; import { ConfigSchema } from '../config'; -import { UnifiedSearchPublicPlugin } from './plugin'; - export type { IndexPatternSelectProps } from './index_pattern_select'; export type { QueryStringInputProps } from './query_string_input'; export { QueryStringInput } from './query_string_input'; export type { StatefulSearchBarProps, SearchBarProps } from './search_bar'; -export type { UnifiedSearchPublicPluginStart } from './types'; +export type { UnifiedSearchPublicPluginStart, UnifiedSearchPluginSetup } from './types'; export { SearchBar } from './search_bar'; export { FilterLabel, FilterItem } from './filter_bar'; export type { ApplyGlobalFilterActionContext } from './actions'; export { ACTION_GLOBAL_APPLY_FILTER } from './actions'; +/* + * Autocomplete query suggestions: + */ +export type { + QuerySuggestion, + QuerySuggestionGetFn, + QuerySuggestionGetFnArgs, + AutocompleteStart, +} from './autocomplete'; + +export { QuerySuggestionTypes } from './autocomplete'; + +import { UnifiedSearchPublicPlugin } from './plugin'; + // This exports static code and TypeScript types, // as well as, Kibana Platform `plugin()` initializer. export function plugin(initializerContext: PluginInitializerContext) { diff --git a/src/plugins/unified_search/public/mocks.ts b/src/plugins/unified_search/public/mocks.ts index eacbb72cc55a..e119c58e89f3 100644 --- a/src/plugins/unified_search/public/mocks.ts +++ b/src/plugins/unified_search/public/mocks.ts @@ -7,12 +7,31 @@ */ import { UnifiedSearchPublicPlugin } from './plugin'; +import { AutocompleteStart, AutocompleteSetup } from './autocomplete'; export type Setup = jest.Mocked>; export type Start = jest.Mocked>; +const autocompleteSetupMock: jest.Mocked = { + getQuerySuggestions: jest.fn(), + getAutocompleteSettings: jest.fn(), +}; + +const autocompleteStartMock: jest.Mocked = { + getValueSuggestions: jest.fn(), + getQuerySuggestions: jest.fn(), + hasQuerySuggestions: jest.fn(), +}; + +const createSetupContract = (): Setup => { + return { + autocomplete: autocompleteSetupMock, + }; +}; + const createStartContract = (): Start => { return { + autocomplete: autocompleteStartMock, ui: { IndexPatternSelect: jest.fn(), SearchBar: jest.fn().mockReturnValue(null), @@ -22,4 +41,5 @@ const createStartContract = (): Start => { export const unifiedSearchPluginMock = { createStartContract, + createSetupContract, }; diff --git a/src/plugins/unified_search/public/plugin.ts b/src/plugins/unified_search/public/plugin.ts index 6064d98ba554..93f1aaf19fae 100755 --- a/src/plugins/unified_search/public/plugin.ts +++ b/src/plugins/unified_search/public/plugin.ts @@ -13,11 +13,16 @@ import { Storage, IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; import type { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public'; import { APPLY_FILTER_TRIGGER } from '@kbn/data-plugin/public'; import { ConfigSchema } from '../config'; -import { setIndexPatterns, setTheme, setOverlays } from './services'; +import { setIndexPatterns, setTheme, setOverlays, setAutocomplete } from './services'; +import { AutocompleteService } from './autocomplete'; import { createSearchBar } from './search_bar'; import { createIndexPatternSelect } from './index_pattern_select'; -import { UnifiedSearchPluginSetup, UnifiedSearchPublicPluginStart } from './types'; -import type { UnifiedSearchStartDependencies, UnifiedSearchSetupDependencies } from './types'; +import type { + UnifiedSearchStartDependencies, + UnifiedSearchSetupDependencies, + UnifiedSearchPluginSetup, + UnifiedSearchPublicPluginStart, +} from './types'; import { createFilterAction } from './actions/apply_filter_action'; import { ACTION_GLOBAL_APPLY_FILTER } from './actions'; @@ -25,22 +30,30 @@ export class UnifiedSearchPublicPlugin implements Plugin { private readonly storage: IStorageWrapper; + private readonly autocomplete: AutocompleteService; private usageCollection: UsageCollectionSetup | undefined; constructor(initializerContext: PluginInitializerContext) { this.storage = new Storage(window.localStorage); + + this.autocomplete = new AutocompleteService(initializerContext); } public setup( - core: CoreSetup, - { uiActions, data }: UnifiedSearchSetupDependencies + core: CoreSetup, + { uiActions, data, usageCollection }: UnifiedSearchSetupDependencies ): UnifiedSearchPluginSetup { const { query } = data; uiActions.registerAction( createFilterAction(query.filterManager, query.timefilter.timefilter, core.theme) ); - return {}; + return { + autocomplete: this.autocomplete.setup(core, { + timefilter: query.timefilter, + usageCollection, + }), + }; } public start( @@ -50,6 +63,8 @@ export class UnifiedSearchPublicPlugin setTheme(core.theme); setOverlays(core.overlays); setIndexPatterns(dataViews); + const autocompleteStart = this.autocomplete.start(); + setAutocomplete(autocompleteStart); const SearchBar = createSearchBar({ core, @@ -68,8 +83,11 @@ export class UnifiedSearchPublicPlugin IndexPatternSelect: createIndexPatternSelect(dataViews), SearchBar, }, + autocomplete: autocompleteStart, }; } - public stop() {} + public stop() { + this.autocomplete.clearProviders(); + } } diff --git a/src/plugins/unified_search/public/query_string_input/query_bar_top_row.test.tsx b/src/plugins/unified_search/public/query_string_input/query_bar_top_row.test.tsx index 373c8a55b9b6..189f12765ad1 100644 --- a/src/plugins/unified_search/public/query_string_input/query_bar_top_row.test.tsx +++ b/src/plugins/unified_search/public/query_string_input/query_bar_top_row.test.tsx @@ -20,6 +20,9 @@ import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; import { I18nProvider } from '@kbn/i18n-react'; import { stubIndexPattern } from '@kbn/data-plugin/public/stubs'; import { UI_SETTINGS } from '@kbn/data-plugin/common'; +import { setAutocomplete } from '../services'; +import { unifiedSearchPluginMock } from '../mocks'; + const startMock = coreMock.createStart(); const mockTimeHistory = { @@ -112,6 +115,11 @@ describe('QueryBarTopRowTopRow', () => { jest.clearAllMocks(); }); + beforeEach(() => { + const autocompleteStart = unifiedSearchPluginMock.createStartContract(); + setAutocomplete(autocompleteStart.autocomplete); + }); + it('Should render query and time picker', () => { const { getByText, getByTestId } = render( wrapQueryBarTopRowInContext({ diff --git a/src/plugins/unified_search/public/query_string_input/query_string_input.test.tsx b/src/plugins/unified_search/public/query_string_input/query_string_input.test.tsx index 39d63a5c888c..b4eed13da7f5 100644 --- a/src/plugins/unified_search/public/query_string_input/query_string_input.test.tsx +++ b/src/plugins/unified_search/public/query_string_input/query_string_input.test.tsx @@ -28,6 +28,9 @@ import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; import { stubIndexPattern } from '@kbn/data-plugin/public/stubs'; import { KibanaContextProvider, withKibana } from '@kbn/kibana-react-plugin/public'; +import { setAutocomplete } from '../services'; +import { unifiedSearchPluginMock } from '../mocks'; + jest.useFakeTimers(); const startMock = coreMock.createStart(); @@ -92,6 +95,11 @@ describe('QueryStringInput', () => { jest.clearAllMocks(); }); + beforeEach(() => { + const autocompleteStart = unifiedSearchPluginMock.createStartContract(); + setAutocomplete(autocompleteStart.autocomplete); + }); + it('Should render the given query', async () => { const { getByText } = render( wrapQueryStringInputInContext({ diff --git a/src/plugins/unified_search/public/query_string_input/query_string_input.tsx b/src/plugins/unified_search/public/query_string_input/query_string_input.tsx index f4b99b8e86fb..a9f4127809ab 100644 --- a/src/plugins/unified_search/public/query_string_input/query_string_input.tsx +++ b/src/plugins/unified_search/public/query_string_input/query_string_input.tsx @@ -28,13 +28,7 @@ import { import { FormattedMessage } from '@kbn/i18n-react'; import { compact, debounce, isEqual, isFunction } from 'lodash'; import { Toast } from '@kbn/core/public'; -import { - IDataPluginServices, - Query, - QuerySuggestion, - QuerySuggestionTypes, - getQueryLog, -} from '@kbn/data-plugin/public'; +import { IDataPluginServices, Query, getQueryLog } from '@kbn/data-plugin/public'; import { DataView } from '@kbn/data-views-plugin/public'; import type { PersistedLog } from '@kbn/data-plugin/public'; import { getFieldSubtypeNested, KIBANA_USER_QUERY_LANGUAGE_KEY } from '@kbn/data-plugin/common'; @@ -47,7 +41,8 @@ import { QueryLanguageSwitcher } from './language_switcher'; import type { SuggestionsListSize } from '../typeahead/suggestions_component'; import { SuggestionsComponent } from '../typeahead'; import { onRaf } from '../utils'; -import { getTheme } from '../services'; +import { QuerySuggestion, QuerySuggestionTypes } from '../autocomplete'; +import { getTheme, getAutocomplete } from '../services'; export interface QueryStringInputProps { indexPatterns: Array; @@ -201,7 +196,7 @@ export default class QueryStringInputUI extends PureComponent { const queryString = this.getQueryString(); const recentSearchSuggestions = this.getRecentSearchSuggestions(queryString); - const hasQuerySuggestions = this.services.data.autocomplete.hasQuerySuggestions(language); + const hasQuerySuggestions = getAutocomplete().hasQuerySuggestions(language); if ( !hasQuerySuggestions || @@ -222,7 +217,7 @@ export default class QueryStringInputUI extends PureComponent { if (this.abortController) this.abortController.abort(); this.abortController = new AbortController(); const suggestions = - (await this.services.data.autocomplete.getQuerySuggestions({ + (await getAutocomplete().getQuerySuggestions({ language, indexPatterns, query: queryString, diff --git a/src/plugins/unified_search/public/services.ts b/src/plugins/unified_search/public/services.ts index 4f8937baf8fd..f67801dd3773 100644 --- a/src/plugins/unified_search/public/services.ts +++ b/src/plugins/unified_search/public/services.ts @@ -9,6 +9,7 @@ import { ThemeServiceStart, OverlayStart } from '@kbn/core/public'; import { createGetterSetter } from '@kbn/kibana-utils-plugin/public'; import { DataViewsContract } from '@kbn/data-views-plugin/public'; +import { AutocompleteStart } from '.'; export const [getIndexPatterns, setIndexPatterns] = createGetterSetter('IndexPatterns'); @@ -16,3 +17,6 @@ export const [getIndexPatterns, setIndexPatterns] = export const [getTheme, setTheme] = createGetterSetter('Theme'); export const [getOverlays, setOverlays] = createGetterSetter('Overlays'); + +export const [getAutocomplete, setAutocomplete] = + createGetterSetter('Autocomplete'); diff --git a/src/plugins/unified_search/public/typeahead/suggestion_component.test.tsx b/src/plugins/unified_search/public/typeahead/suggestion_component.test.tsx index d32ce1db3bdf..31c38ad94815 100644 --- a/src/plugins/unified_search/public/typeahead/suggestion_component.test.tsx +++ b/src/plugins/unified_search/public/typeahead/suggestion_component.test.tsx @@ -8,7 +8,7 @@ import { mount, shallow } from 'enzyme'; import React from 'react'; -import { QuerySuggestion, QuerySuggestionTypes } from '@kbn/data-plugin/public'; +import { QuerySuggestion, QuerySuggestionTypes } from '../autocomplete'; import { SuggestionComponent } from './suggestion_component'; const noop = () => { diff --git a/src/plugins/unified_search/public/typeahead/suggestion_component.tsx b/src/plugins/unified_search/public/typeahead/suggestion_component.tsx index 9fd4f0ddb965..b8b3f2640c31 100644 --- a/src/plugins/unified_search/public/typeahead/suggestion_component.tsx +++ b/src/plugins/unified_search/public/typeahead/suggestion_component.tsx @@ -9,7 +9,7 @@ import { EuiIcon } from '@elastic/eui'; import classNames from 'classnames'; import React, { useCallback } from 'react'; -import { QuerySuggestion } from '@kbn/data-plugin/public'; +import { QuerySuggestion } from '../autocomplete'; import { SuggestionOnClick, SuggestionOnMouseEnter } from './types'; function getEuiIconType(type: string) { diff --git a/src/plugins/unified_search/public/typeahead/suggestions_component.test.tsx b/src/plugins/unified_search/public/typeahead/suggestions_component.test.tsx index 7f1715ed2d70..6a21041f6953 100644 --- a/src/plugins/unified_search/public/typeahead/suggestions_component.test.tsx +++ b/src/plugins/unified_search/public/typeahead/suggestions_component.test.tsx @@ -8,7 +8,7 @@ import { mount, shallow } from 'enzyme'; import React from 'react'; -import { QuerySuggestion, QuerySuggestionTypes } from '@kbn/data-plugin/public'; +import { QuerySuggestion, QuerySuggestionTypes } from '../autocomplete'; import { SuggestionComponent } from './suggestion_component'; import SuggestionsComponent from './suggestions_component'; diff --git a/src/plugins/unified_search/public/typeahead/suggestions_component.tsx b/src/plugins/unified_search/public/typeahead/suggestions_component.tsx index e9d5d51050f9..75e446cf2d6e 100644 --- a/src/plugins/unified_search/public/typeahead/suggestions_component.tsx +++ b/src/plugins/unified_search/public/typeahead/suggestions_component.tsx @@ -13,7 +13,7 @@ import classNames from 'classnames'; import styled from 'styled-components'; import useRafState from 'react-use/lib/useRafState'; -import { QuerySuggestion } from '@kbn/data-plugin/public'; +import { QuerySuggestion } from '../autocomplete'; import { SuggestionComponent } from './suggestion_component'; import { SUGGESTIONS_LIST_REQUIRED_BOTTOM_SPACE, diff --git a/src/plugins/unified_search/public/typeahead/types.ts b/src/plugins/unified_search/public/typeahead/types.ts index 0da396ae56d7..1a58c29d028e 100644 --- a/src/plugins/unified_search/public/typeahead/types.ts +++ b/src/plugins/unified_search/public/typeahead/types.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { QuerySuggestion } from '@kbn/data-plugin/public'; +import { QuerySuggestion } from '../autocomplete'; export type SuggestionOnClick = (suggestion: QuerySuggestion, index: number) => void; diff --git a/src/plugins/unified_search/public/types.ts b/src/plugins/unified_search/public/types.ts index e113fd32f54e..29cf59f41a87 100755 --- a/src/plugins/unified_search/public/types.ts +++ b/src/plugins/unified_search/public/types.ts @@ -5,19 +5,24 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ + import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; import type { UiActionsSetup, UiActionsStart } from '@kbn/ui-actions-plugin/public'; -import { IndexPatternSelectProps, StatefulSearchBarProps } from '.'; +import { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public'; +import { AutocompleteSetup, AutocompleteStart } from './autocomplete'; +import type { IndexPatternSelectProps, StatefulSearchBarProps } from '.'; export interface UnifiedSearchSetupDependencies { uiActions: UiActionsSetup; data: DataPublicPluginStart; + usageCollection?: UsageCollectionSetup; } -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface UnifiedSearchPluginSetup {} +export interface UnifiedSearchPluginSetup { + autocomplete: AutocompleteSetup; +} export interface UnifiedSearchStartDependencies { dataViews: DataViewsPublicPluginStart; @@ -38,6 +43,11 @@ export interface UnifiedSearchPublicPluginStartUi { * Unified search plugin public Start contract */ export interface UnifiedSearchPublicPluginStart { + /** + * autocomplete service + * {@link AutocompleteStart} + */ + autocomplete: AutocompleteStart; /** * prewired UI components * {@link DataPublicPluginStartUi} diff --git a/src/plugins/data/server/autocomplete/autocomplete_service.ts b/src/plugins/unified_search/server/autocomplete/autocomplete_service.ts similarity index 100% rename from src/plugins/data/server/autocomplete/autocomplete_service.ts rename to src/plugins/unified_search/server/autocomplete/autocomplete_service.ts diff --git a/src/plugins/data/server/autocomplete/index.ts b/src/plugins/unified_search/server/autocomplete/index.ts similarity index 100% rename from src/plugins/data/server/autocomplete/index.ts rename to src/plugins/unified_search/server/autocomplete/index.ts diff --git a/src/plugins/data/server/autocomplete/routes.ts b/src/plugins/unified_search/server/autocomplete/routes.ts similarity index 100% rename from src/plugins/data/server/autocomplete/routes.ts rename to src/plugins/unified_search/server/autocomplete/routes.ts diff --git a/src/plugins/data/server/autocomplete/terms_agg.test.ts b/src/plugins/unified_search/server/autocomplete/terms_agg.test.ts similarity index 100% rename from src/plugins/data/server/autocomplete/terms_agg.test.ts rename to src/plugins/unified_search/server/autocomplete/terms_agg.test.ts diff --git a/src/plugins/data/server/autocomplete/terms_agg.ts b/src/plugins/unified_search/server/autocomplete/terms_agg.ts similarity index 97% rename from src/plugins/data/server/autocomplete/terms_agg.ts rename to src/plugins/unified_search/server/autocomplete/terms_agg.ts index 7e48bc6ee21c..ffdaca8caad4 100644 --- a/src/plugins/data/server/autocomplete/terms_agg.ts +++ b/src/plugins/unified_search/server/autocomplete/terms_agg.ts @@ -9,8 +9,8 @@ import { get, map } from 'lodash'; import { ElasticsearchClient, SavedObjectsClientContract } from '@kbn/core/server'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { IFieldType, getFieldSubtypeNested } from '@kbn/data-plugin/common'; import { ConfigSchema } from '../../config'; -import { IFieldType, getFieldSubtypeNested } from '../../common'; import { findIndexPatternById, getFieldByName } from '../data_views'; export async function termsAggSuggestions( diff --git a/src/plugins/data/server/autocomplete/terms_enum.test.ts b/src/plugins/unified_search/server/autocomplete/terms_enum.test.ts similarity index 100% rename from src/plugins/data/server/autocomplete/terms_enum.test.ts rename to src/plugins/unified_search/server/autocomplete/terms_enum.test.ts diff --git a/src/plugins/data/server/autocomplete/terms_enum.ts b/src/plugins/unified_search/server/autocomplete/terms_enum.ts similarity index 95% rename from src/plugins/data/server/autocomplete/terms_enum.ts rename to src/plugins/unified_search/server/autocomplete/terms_enum.ts index 77e8887f3fcb..924b5b3a1671 100644 --- a/src/plugins/data/server/autocomplete/terms_enum.ts +++ b/src/plugins/unified_search/server/autocomplete/terms_enum.ts @@ -8,7 +8,7 @@ import { ElasticsearchClient, SavedObjectsClientContract } from '@kbn/core/server'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import { FieldSpec } from '../../common'; +import { IFieldType } from '@kbn/data-plugin/common'; import { findIndexPatternById, getFieldByName } from '../data_views'; import { ConfigSchema } from '../../config'; @@ -20,7 +20,7 @@ export async function termsEnumSuggestions( fieldName: string, query: string, filters?: estypes.QueryDslQueryContainer[], - field?: FieldSpec, + field?: IFieldType, abortSignal?: AbortSignal ) { const { tiers } = config.autocomplete.valueSuggestions; diff --git a/src/plugins/data/server/autocomplete/value_suggestions_route.ts b/src/plugins/unified_search/server/autocomplete/value_suggestions_route.ts similarity index 91% rename from src/plugins/data/server/autocomplete/value_suggestions_route.ts rename to src/plugins/unified_search/server/autocomplete/value_suggestions_route.ts index 9c6c037a86a8..74a7e5202a54 100644 --- a/src/plugins/data/server/autocomplete/value_suggestions_route.ts +++ b/src/plugins/unified_search/server/autocomplete/value_suggestions_route.ts @@ -9,8 +9,8 @@ import { schema } from '@kbn/config-schema'; import { IRouter } from '@kbn/core/server'; import { firstValueFrom, Observable } from 'rxjs'; +import { getRequestAbortedSignal } from '@kbn/data-plugin/server'; import { getKbnServerError, reportServerError } from '@kbn/kibana-utils-plugin/server'; -import { getRequestAbortedSignal } from '../lib'; import type { ConfigSchema } from '../../config'; import { termsEnumSuggestions } from './terms_enum'; import { termsAggSuggestions } from './terms_agg'; @@ -45,13 +45,14 @@ export function registerValueSuggestionsRoute(router: IRouter, config$: Observab const { field: fieldName, query, filters, fieldMeta, method } = request.body; const { index } = request.params; const abortSignal = getRequestAbortedSignal(request.events.aborted$); + const { savedObjects, elasticsearch } = await context.core; try { const fn = method === 'terms_agg' ? termsAggSuggestions : termsEnumSuggestions; const body = await fn( config, - context.core.savedObjects.client, - context.core.elasticsearch.client.asCurrentUser, + savedObjects.client, + elasticsearch.client.asCurrentUser, index, fieldName, query, diff --git a/src/plugins/unified_search/server/config_deprecations.test.ts b/src/plugins/unified_search/server/config_deprecations.test.ts new file mode 100644 index 000000000000..8c65acfae629 --- /dev/null +++ b/src/plugins/unified_search/server/config_deprecations.test.ts @@ -0,0 +1,137 @@ +/* + * 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 { cloneDeep } from 'lodash'; + +import { applyDeprecations, configDeprecationFactory } from '@kbn/config'; +import { configDeprecationsMock } from '@kbn/core/server/mocks'; + +import { autocompleteConfigDeprecationProvider } from './config_deprecations'; + +const deprecationContext = configDeprecationsMock.createContext(); + +const applyConfigDeprecations = (settings: Record = {}) => { + const deprecations = autocompleteConfigDeprecationProvider(configDeprecationFactory); + const deprecationMessages: string[] = []; + const migrated = applyDeprecations( + settings, + deprecations.map((deprecation) => ({ + deprecation, + path: '', + context: deprecationContext, + })), + () => + ({ message }) => + deprecationMessages.push(message) + ); + return { + messages: deprecationMessages, + migrated: migrated.config, + }; +}; + +describe('Config Deprecations', () => { + it('does not report deprecations for default configurationc', () => { + const configFirstStep = { data: { autocomplete: { valueSuggestions: {} } } }; + const { messages, migrated } = applyConfigDeprecations(cloneDeep(configFirstStep)); + expect(migrated).toEqual(configFirstStep); + expect(messages).toHaveLength(0); + }); + + it('renames kibana.autocompleteTerminateAfter to unifiedSearch.autocomplete.valueSuggestions.terminateAfter', () => { + const config = { + kibana: { + autocompleteTerminateAfter: 123, + }, + }; + const { messages, migrated } = applyConfigDeprecations(cloneDeep(config)); + expect(migrated.kibana?.autocompleteTerminateAfter).not.toBeDefined(); + expect(migrated.unifiedSearch.autocomplete.valueSuggestions.terminateAfter).toEqual(123); + expect(messages).toMatchInlineSnapshot(` + Array [ + "Setting \\"kibana.autocompleteTerminateAfter\\" has been replaced by \\"unifiedSearch.autocomplete.valueSuggestions.terminateAfter\\"", + ] + `); + }); + + it('renames kibana.autocompleteTimeout to unifiedSearch.autocomplete.valueSuggestions.timeout', () => { + const config = { + kibana: { + autocompleteTimeout: 123, + }, + }; + const { messages, migrated } = applyConfigDeprecations(cloneDeep(config)); + expect(migrated.kibana?.autocompleteTimeout).not.toBeDefined(); + expect(migrated.unifiedSearch.autocomplete.valueSuggestions.timeout).toEqual(123); + expect(messages).toMatchInlineSnapshot(` + Array [ + "Setting \\"kibana.autocompleteTimeout\\" has been replaced by \\"unifiedSearch.autocomplete.valueSuggestions.timeout\\"", + ] + `); + }); + + it('renames data.autocomplete.querySuggestions.enabled to unifiedSearch.autocomplete.querySuggestions.enabled', () => { + const config = { + data: { + autocomplete: { + querySuggestions: { + enabled: false, + }, + }, + }, + }; + const { messages, migrated } = applyConfigDeprecations(cloneDeep(config)); + expect(migrated.data?.autocomplete.querySuggestions.enabled).not.toBeDefined(); + expect(migrated.unifiedSearch.autocomplete.querySuggestions.enabled).toEqual(false); + expect(messages).toMatchInlineSnapshot(` + Array [ + "Setting \\"data.autocomplete.querySuggestions.enabled\\" has been replaced by \\"unifiedSearch.autocomplete.querySuggestions.enabled\\"", + ] + `); + }); + + it('renames data.autocomplete.valueSuggestions.enabled to unifiedSearch.autocomplete.valueSuggestions.enabled', () => { + const config = { + data: { + autocomplete: { + valueSuggestions: { + enabled: false, + }, + }, + }, + }; + const { messages, migrated } = applyConfigDeprecations(cloneDeep(config)); + expect(migrated.data?.autocomplete.valueSuggestions.enabled).not.toBeDefined(); + expect(migrated.unifiedSearch.autocomplete.valueSuggestions.enabled).toEqual(false); + expect(messages).toMatchInlineSnapshot(` + Array [ + "Setting \\"data.autocomplete.valueSuggestions.enabled\\" has been replaced by \\"unifiedSearch.autocomplete.valueSuggestions.enabled\\"", + ] + `); + }); + + it('renames data.autocomplete.valueSuggestions.tiers to unifiedSearch.autocomplete.valueSuggestions.tiers', () => { + const config = { + data: { + autocomplete: { + valueSuggestions: { + tiers: [], + }, + }, + }, + }; + const { messages, migrated } = applyConfigDeprecations(cloneDeep(config)); + expect(migrated.data?.autocomplete.valueSuggestions.tiers).not.toBeDefined(); + expect(migrated.unifiedSearch.autocomplete.valueSuggestions.tiers).toEqual([]); + expect(messages).toMatchInlineSnapshot(` + Array [ + "Setting \\"data.autocomplete.valueSuggestions.tiers\\" has been replaced by \\"unifiedSearch.autocomplete.valueSuggestions.tiers\\"", + ] + `); + }); +}); diff --git a/src/plugins/unified_search/server/config_deprecations.ts b/src/plugins/unified_search/server/config_deprecations.ts new file mode 100644 index 000000000000..de083caffad0 --- /dev/null +++ b/src/plugins/unified_search/server/config_deprecations.ts @@ -0,0 +1,59 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 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 { ConfigDeprecationProvider } from '@kbn/core/server'; + +export const autocompleteConfigDeprecationProvider: ConfigDeprecationProvider = ({ + renameFromRoot, +}) => [ + renameFromRoot( + 'data.autocomplete.valueSuggestions.terminateAfter', + 'unifiedSearch.autocomplete.valueSuggestions.terminateAfter', + { level: 'warning' } + ), + renameFromRoot( + 'kibana.autocompleteTerminateAfter', + 'unifiedSearch.autocomplete.valueSuggestions.terminateAfter', + { level: 'warning' } + ), + renameFromRoot( + 'data.autocomplete.valueSuggestions.timeout', + 'unifiedSearch.autocomplete.valueSuggestions.timeout', + { + level: 'warning', + } + ), + renameFromRoot( + 'kibana.autocompleteTimeout', + 'unifiedSearch.autocomplete.valueSuggestions.timeout', + { + level: 'warning', + } + ), + renameFromRoot( + 'data.autocomplete.querySuggestions.enabled', + 'unifiedSearch.autocomplete.querySuggestions.enabled', + { + level: 'warning', + } + ), + renameFromRoot( + 'data.autocomplete.valueSuggestions.enabled', + 'unifiedSearch.autocomplete.valueSuggestions.enabled', + { + level: 'warning', + } + ), + renameFromRoot( + 'data.autocomplete.valueSuggestions.tiers', + 'unifiedSearch.autocomplete.valueSuggestions.tiers', + { + level: 'warning', + } + ), +]; diff --git a/src/plugins/unified_search/server/data_views/index.ts b/src/plugins/unified_search/server/data_views/index.ts new file mode 100644 index 000000000000..dfe03378f027 --- /dev/null +++ b/src/plugins/unified_search/server/data_views/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 * from '@kbn/data-views-plugin/server'; diff --git a/src/plugins/unified_search/server/index.ts b/src/plugins/unified_search/server/index.ts new file mode 100644 index 000000000000..d1948f32e2fe --- /dev/null +++ b/src/plugins/unified_search/server/index.ts @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 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 { PluginConfigDescriptor, PluginInitializerContext } from '@kbn/core/server'; +import { ConfigSchema, configSchema } from '../config'; +import { + UnifiedSearchServerPlugin, + UnifiedSearchServerPluginSetup, + UnifiedSearchServerPluginStart, +} from './plugin'; + +import { autocompleteConfigDeprecationProvider } from './config_deprecations'; + +/** + * Static code to be shared externally + * @public + */ + +export function plugin(initializerContext: PluginInitializerContext) { + return new UnifiedSearchServerPlugin(initializerContext); +} + +export type { + UnifiedSearchServerPluginSetup as PluginSetup, + UnifiedSearchServerPluginStart as PluginStart, +}; +export { UnifiedSearchServerPlugin as Plugin }; + +export const config: PluginConfigDescriptor = { + deprecations: autocompleteConfigDeprecationProvider, + exposeToBrowser: { + autocomplete: true, + }, + schema: configSchema, +}; diff --git a/src/plugins/unified_search/server/mocks.ts b/src/plugins/unified_search/server/mocks.ts new file mode 100644 index 000000000000..7f63abcdaae0 --- /dev/null +++ b/src/plugins/unified_search/server/mocks.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 { AutocompleteSetup } from './autocomplete'; + +const autocompleteSetupMock: jest.Mocked = { + getAutocompleteSettings: jest.fn(), +}; + +function createSetupContract() { + return { + autocomplete: autocompleteSetupMock, + }; +} + +export const dataPluginMock = { + createSetupContract, +}; diff --git a/src/plugins/unified_search/server/plugin.ts b/src/plugins/unified_search/server/plugin.ts new file mode 100644 index 000000000000..6328eeba2f41 --- /dev/null +++ b/src/plugins/unified_search/server/plugin.ts @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 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 { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from '@kbn/core/server'; +import { ConfigSchema } from '../config'; +import { AutocompleteService } from './autocomplete'; +import { AutocompleteSetup } from './autocomplete/autocomplete_service'; + +export interface UnifiedSearchServerPluginSetup { + autocomplete: AutocompleteSetup; +} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface UnifiedSearchServerPluginStart {} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface UnifiedSearchServerPluginSetupDependencies {} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface UnifiedSearchServerPluginStartDependencies {} + +export class UnifiedSearchServerPlugin implements Plugin { + private readonly autocompleteService: AutocompleteService; + + constructor(initializerContext: PluginInitializerContext) { + this.autocompleteService = new AutocompleteService(initializerContext); + } + + public setup( + core: CoreSetup, + {}: UnifiedSearchServerPluginSetupDependencies + ) { + return { + autocomplete: this.autocompleteService.setup(core), + }; + } + + public start(core: CoreStart, {}: UnifiedSearchServerPluginStartDependencies) { + return {}; + } + + public stop() {} +} + +export { UnifiedSearchServerPlugin as Plugin }; diff --git a/src/plugins/unified_search/tsconfig.json b/src/plugins/unified_search/tsconfig.json index 3ff1ba630ac2..41dc76f1305b 100644 --- a/src/plugins/unified_search/tsconfig.json +++ b/src/plugins/unified_search/tsconfig.json @@ -9,7 +9,9 @@ "include": [ "public/**/*", "config.ts", - "public/**/*.json" + "public/**/*.json", + "server/**/*", + "config.ts", ], "references": [ { "path": "../../core/tsconfig.json" }, diff --git a/src/plugins/usage_collection/server/routes/stats/stats.ts b/src/plugins/usage_collection/server/routes/stats/stats.ts index 2c6c8e77d8cd..d9c27afb5fb2 100644 --- a/src/plugins/usage_collection/server/routes/stats/stats.ts +++ b/src/plugins/usage_collection/server/routes/stats/stats.ts @@ -89,8 +89,9 @@ export function registerStatsRoute({ let extended; if (isExtended) { - const { asCurrentUser } = context.core.elasticsearch.client; - const savedObjectsClient = context.core.savedObjects.client; + const core = await context.core; + const { asCurrentUser } = core.elasticsearch.client; + const savedObjectsClient = core.savedObjects.client; const [usage, clusterUuid] = await Promise.all([ shouldGetUsage diff --git a/src/plugins/vis_types/timelion/server/routes/run.ts b/src/plugins/vis_types/timelion/server/routes/run.ts index 5fb6ef6c0a3d..e136f1ab5a24 100644 --- a/src/plugins/vis_types/timelion/server/routes/run.ts +++ b/src/plugins/vis_types/timelion/server/routes/run.ts @@ -77,10 +77,11 @@ export function runRoute( }, router.handleLegacyErrors(async (context, request, response) => { const [, { dataViews }] = await core.getStartServices(); - const uiSettings = await context.core.uiSettings.client.getAll(); + const coreCtx = await context.core; + const uiSettings = await coreCtx.uiSettings.client.getAll(); const indexPatternsService = await dataViews.dataViewsServiceFactory( - context.core.savedObjects.client, - context.core.elasticsearch.client.asCurrentUser + coreCtx.savedObjects.client, + coreCtx.elasticsearch.client.asCurrentUser ); const tlConfig = getTlConfig({ diff --git a/src/plugins/vis_types/timelion/server/series_functions/es/index.js b/src/plugins/vis_types/timelion/server/series_functions/es/index.js index a86ee64f0056..2001a39bf790 100644 --- a/src/plugins/vis_types/timelion/server/series_functions/es/index.js +++ b/src/plugins/vis_types/timelion/server/series_functions/es/index.js @@ -117,7 +117,8 @@ export default new Datasource('es', { // we need to handle this scenario by aborting underlying server requests const abortSignal = getRequestAbortedSignal(tlConfig.request.events.aborted$); - const resp = await tlConfig.context.search + const searchContext = await tlConfig.context.search; + const resp = await searchContext .search( body, { diff --git a/src/plugins/vis_types/timeseries/server/lib/get_vis_data.ts b/src/plugins/vis_types/timeseries/server/lib/get_vis_data.ts index a76132e0fbd2..360256ccb628 100644 --- a/src/plugins/vis_types/timeseries/server/lib/get_vis_data.ts +++ b/src/plugins/vis_types/timeseries/server/lib/get_vis_data.ts @@ -28,7 +28,7 @@ export async function getVisData( request: VisTypeTimeseriesVisDataRequest, framework: Framework ): Promise { - const uiSettings = requestContext.core.uiSettings.client; + const uiSettings = (await requestContext.core).uiSettings.client; const esShardTimeout = await framework.getEsShardTimeout(); const fieldFormatService = await framework.getFieldFormatsService(uiSettings); const indexPatternsService = await framework.getIndexPatternsService(requestContext); diff --git a/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.ts b/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.ts index ae5dade8c559..9fb7fc2a3231 100644 --- a/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.ts +++ b/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.ts @@ -84,7 +84,7 @@ describe('AbstractSearchStrategy', () => { ); expect(responses).toEqual([{}]); - expect(requestContext.search.search).toHaveBeenCalledWith( + expect((await requestContext.search).search).toHaveBeenCalledWith( { params: { body: {}, diff --git a/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts b/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts index e89e999e583f..167bc9cec8e0 100644 --- a/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts +++ b/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts @@ -46,11 +46,12 @@ export abstract class AbstractSearchStrategy { // User may abort the request without waiting for the results // we need to handle this scenario by aborting underlying server requests const abortSignal = getRequestAbortedSignal(req.events.aborted$); + const searchContext = await requestContext.search; esRequests.forEach(({ body, index, trackingEsSearchMeta }) => { const startTime = Date.now(); requests.push( - requestContext.search + searchContext .search( { indexType, diff --git a/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts b/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts index f2c52b59bf0d..ca20f87641f8 100644 --- a/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts +++ b/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/default_search_strategy.ts @@ -22,7 +22,7 @@ export class DefaultSearchStrategy extends AbstractSearchStrategy { requestContext: VisTypeTimeseriesRequestHandlerContext, req: VisTypeTimeseriesRequest ) { - const uiSettings = requestContext.core.uiSettings.client; + const uiSettings = (await requestContext.core).uiSettings.client; return { isViable: true, diff --git a/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts b/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts index dbc98e624b27..35223f5159a8 100644 --- a/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts +++ b/src/plugins/vis_types/timeseries/server/lib/search_strategies/strategies/rollup_search_strategy.ts @@ -38,10 +38,10 @@ export class RollupSearchStrategy extends AbstractSearchStrategy { indexPattern: string ) { try { - const body = - await requestContext.core.elasticsearch.client.asCurrentUser.rollup.getRollupIndexCaps({ - index: indexPattern, - }); + const esClient = (await requestContext.core).elasticsearch.client; + const body = await esClient.asCurrentUser.rollup.getRollupIndexCaps({ + index: indexPattern, + }); return body; } catch (e) { @@ -64,7 +64,7 @@ export class RollupSearchStrategy extends AbstractSearchStrategy { ) { const rollupData = await this.getRollupData(requestContext, indexPatternString); const rollupIndices = getRollupIndices(rollupData); - const uiSettings = requestContext.core.uiSettings.client; + const uiSettings = (await requestContext.core).uiSettings.client; isViable = rollupIndices.length === 1; diff --git a/src/plugins/vis_types/timeseries/server/plugin.ts b/src/plugins/vis_types/timeseries/server/plugin.ts index e30758745627..31ca2667ed3a 100644 --- a/src/plugins/vis_types/timeseries/server/plugin.ts +++ b/src/plugins/vis_types/timeseries/server/plugin.ts @@ -109,10 +109,10 @@ export class VisTypeTimeseriesPlugin implements Plugin { ), getIndexPatternsService: async (requestContext) => { const [, { dataViews }] = await core.getStartServices(); - + const { elasticsearch, savedObjects } = await requestContext.core; return await dataViews.dataViewsServiceFactory( - requestContext.core.savedObjects.client, - requestContext.core.elasticsearch.client.asCurrentUser + savedObjects.client, + elasticsearch.client.asCurrentUser ); }, getFieldFormatsService: async (uiSettings) => { diff --git a/test/functional/apps/console/_autocomplete.ts b/test/functional/apps/console/_autocomplete.ts index 57c59793f69f..7bf872373c6c 100644 --- a/test/functional/apps/console/_autocomplete.ts +++ b/test/functional/apps/console/_autocomplete.ts @@ -27,6 +27,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should provide basic auto-complete functionality', async () => { await PageObjects.console.enterRequest(); + await PageObjects.console.pressEnter(); await PageObjects.console.enterText(`{\n\t"query": {`); await PageObjects.console.pressEnter(); await PageObjects.console.promptAutocomplete(); @@ -39,6 +40,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { beforeEach(async () => { await PageObjects.console.clearTextArea(); await PageObjects.console.enterRequest(); + await PageObjects.console.pressEnter(); }); it('should add a comma after previous non empty line', async () => { await PageObjects.console.enterText(`{\n\t"query": {\n\t\t"match": {}`); @@ -85,6 +87,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { beforeEach(async () => { await PageObjects.console.clearTextArea(); await PageObjects.console.enterRequest('\n POST _snapshot/test_repo'); + await PageObjects.console.pressEnter(); }); await asyncForEach(CONDITIONAL_TEMPLATES, async ({ type, template }) => { diff --git a/test/functional/apps/console/_console.ts b/test/functional/apps/console/_console.ts index 367f1ccb5625..52218b88be60 100644 --- a/test/functional/apps/console/_console.ts +++ b/test/functional/apps/console/_console.ts @@ -106,5 +106,21 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); }); }); + + describe('with kbn: prefix in request', () => { + before(async () => { + await PageObjects.console.clearTextArea(); + }); + it('it should send successful request to Kibana API', async () => { + const expectedResponseContains = 'default space'; + await PageObjects.console.enterRequest('\n GET kbn:/api/spaces/space'); + await PageObjects.console.clickPlay(); + await retry.try(async () => { + const actualResponse = await PageObjects.console.getResponse(); + log.debug(actualResponse); + expect(actualResponse).to.contain(expectedResponseContains); + }); + }); + }); }); } diff --git a/test/functional/apps/dashboard/dashboard_unsaved_listing.ts b/test/functional/apps/dashboard/dashboard_unsaved_listing.ts index d9451eda5ff6..a1db57784b5f 100644 --- a/test/functional/apps/dashboard/dashboard_unsaved_listing.ts +++ b/test/functional/apps/dashboard/dashboard_unsaved_listing.ts @@ -106,7 +106,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('can discard unsaved changes using the discard link', async () => { - await PageObjects.dashboard.clickUnsavedChangesDiscard(dashboardTitle); + await PageObjects.dashboard.clickUnsavedChangesDiscard( + `discard-unsaved-${dashboardTitle.split(' ').join('-')}` + ); await PageObjects.dashboard.expectUnsavedChangesDoesNotExist(dashboardTitle); await PageObjects.dashboard.loadSavedDashboard(dashboardTitle); await PageObjects.dashboard.switchToEditMode(); diff --git a/test/functional/apps/dashboard_elements/controls/options_list.ts b/test/functional/apps/dashboard_elements/controls/options_list.ts index 39522c43fc84..a4da1c217f92 100644 --- a/test/functional/apps/dashboard_elements/controls/options_list.ts +++ b/test/functional/apps/dashboard_elements/controls/options_list.ts @@ -116,6 +116,36 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); }); + it('editing field clears selections', async () => { + const secondId = (await dashboardControls.getAllControlIds())[1]; + await dashboardControls.optionsListOpenPopover(secondId); + await dashboardControls.optionsListPopoverSelectOption('hiss'); + await dashboardControls.optionsListEnsurePopoverIsClosed(secondId); + + await dashboardControls.editExistingControl(secondId); + await dashboardControls.controlsEditorSetfield('animal.keyword'); + await dashboardControls.controlEditorSave(); + + const selectionString = await dashboardControls.optionsListGetSelectionsString(secondId); + expect(selectionString).to.be('Select...'); + }); + + it('editing other control settings keeps selections', async () => { + const secondId = (await dashboardControls.getAllControlIds())[1]; + await dashboardControls.optionsListOpenPopover(secondId); + await dashboardControls.optionsListPopoverSelectOption('dog'); + await dashboardControls.optionsListPopoverSelectOption('cat'); + await dashboardControls.optionsListEnsurePopoverIsClosed(secondId); + + await dashboardControls.editExistingControl(secondId); + await dashboardControls.controlEditorSetTitle('Animal'); + await dashboardControls.controlEditorSetWidth('large'); + await dashboardControls.controlEditorSave(); + + const selectionString = await dashboardControls.optionsListGetSelectionsString(secondId); + expect(selectionString).to.be('dog, cat'); + }); + it('deletes an existing control', async () => { const firstId = (await dashboardControls.getAllControlIds())[0]; diff --git a/test/functional/apps/dashboard_elements/controls/range_slider.ts b/test/functional/apps/dashboard_elements/controls/range_slider.ts index 27f1244120a7..1ce8f05cae19 100644 --- a/test/functional/apps/dashboard_elements/controls/range_slider.ts +++ b/test/functional/apps/dashboard_elements/controls/range_slider.ts @@ -25,6 +25,20 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { 'header', ]); + const validateRange = async ( + compare: 'value' | 'placeholder', // if 'value', compare actual selections; otherwise, compare the default range + controlId: string, + expectedLowerBound: string, + expectedUpperBound: string + ) => { + expect(await dashboardControls.rangeSliderGetLowerBoundAttribute(controlId, compare)).to.be( + expectedLowerBound + ); + expect(await dashboardControls.rangeSliderGetUpperBoundAttribute(controlId, compare)).to.be( + expectedUpperBound + ); + }; + describe('Range Slider Control', async () => { before(async () => { await security.testUser.setRoles([ @@ -82,12 +96,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); expect(await dashboardControls.getControlsCount()).to.be(2); const secondId = (await dashboardControls.getAllControlIds())[1]; - expect( - await dashboardControls.rangeSliderGetLowerBoundAttribute(secondId, 'placeholder') - ).to.be('100'); - expect( - await dashboardControls.rangeSliderGetUpperBoundAttribute(secondId, 'placeholder') - ).to.be('1200'); + validateRange('placeholder', secondId, '100', '1200'); // data views should be properly propagated from the control group to the dashboard expect(await filterBar.getIndexPatterns()).to.be('logstash-*,kibana_sample_data_flights'); }); @@ -112,12 +121,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await dashboardControls.controlsEditorSetfield('dayOfWeek'); await dashboardControls.controlEditorSave(); await dashboardControls.rangeSliderWaitForLoading(); - expect( - await dashboardControls.rangeSliderGetLowerBoundAttribute(firstId, 'placeholder') - ).to.be('0'); - expect( - await dashboardControls.rangeSliderGetUpperBoundAttribute(firstId, 'placeholder') - ).to.be('6'); + validateRange('placeholder', firstId, '0', '6'); + // when creating a new filter, the ability to select a data view should be removed, because the dashboard now only has one data view await retry.try(async () => { await testSubjects.click('addFilter'); @@ -150,31 +155,38 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('applies filter from the first control on the second control', async () => { await dashboardControls.rangeSliderWaitForLoading(); const secondId = (await dashboardControls.getAllControlIds())[1]; - const availableMin = await dashboardControls.rangeSliderGetLowerBoundAttribute( - secondId, - 'placeholder' - ); - expect(availableMin).to.be('100'); - const availabeMax = await dashboardControls.rangeSliderGetUpperBoundAttribute( - secondId, - 'placeholder' - ); - expect(availabeMax).to.be('1000'); + validateRange('placeholder', secondId, '100', '1000'); + }); + + it('editing field clears selections', async () => { + const secondId = (await dashboardControls.getAllControlIds())[1]; + await dashboardControls.editExistingControl(secondId); + await dashboardControls.controlsEditorSetfield('FlightDelayMin'); + await dashboardControls.controlEditorSave(); + + await dashboardControls.rangeSliderWaitForLoading(); + validateRange('value', secondId, '', ''); + }); + + it('editing other control settings keeps selections', async () => { + const secondId = (await dashboardControls.getAllControlIds())[1]; + await dashboardControls.rangeSliderSetLowerBound(secondId, '50'); + await dashboardControls.rangeSliderSetUpperBound(secondId, '100'); + await dashboardControls.rangeSliderWaitForLoading(); + + await dashboardControls.editExistingControl(secondId); + await dashboardControls.controlEditorSetTitle('Minimum Flight Delay'); + await dashboardControls.controlEditorSetWidth('large'); + await dashboardControls.controlEditorSave(); + + await dashboardControls.rangeSliderWaitForLoading(); + validateRange('value', secondId, '50', '100'); }); it('can clear out selections by clicking the reset button', async () => { const firstId = (await dashboardControls.getAllControlIds())[0]; await dashboardControls.rangeSliderClearSelection(firstId); - const lowerBoundSelection = await dashboardControls.rangeSliderGetLowerBoundAttribute( - firstId, - 'value' - ); - expect(lowerBoundSelection.length).to.be(0); - const upperBoundSelection = await dashboardControls.rangeSliderGetUpperBoundAttribute( - firstId, - 'value' - ); - expect(upperBoundSelection.length).to.be(0); + validateRange('value', firstId, '', ''); }); it('deletes an existing control', async () => { diff --git a/test/functional/apps/discover/_filter_editor.ts b/test/functional/apps/discover/_filter_editor.ts index 0fb883759890..b2fbfbb71c7e 100644 --- a/test/functional/apps/discover/_filter_editor.ts +++ b/test/functional/apps/discover/_filter_editor.ts @@ -16,6 +16,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const kibanaServer = getService('kibanaServer'); const filterBar = getService('filterBar'); + const security = getService('security'); const PageObjects = getPageObjects(['common', 'discover', 'timePicker']); const defaultSettings = { defaultIndex: 'logstash-*', @@ -23,6 +24,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { describe('discover filter editor', function describeIndexTests() { before(async function () { + await security.testUser.setRoles(['kibana_admin', 'version_test', 'test_logstash_reader']); log.debug('load kibana index with default index pattern'); await kibanaServer.savedObjects.clean({ types: ['search', 'index-pattern'] }); await kibanaServer.importExport.load('test/functional/fixtures/kbn_archiver/discover.json'); @@ -111,5 +113,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); }); }); + + after(async () => { + await security.testUser.restoreDefaults(); + }); }); } diff --git a/test/functional/apps/visualize/_tsvb_time_series.ts b/test/functional/apps/visualize/_tsvb_time_series.ts index c27d2281d611..3f6661aaecf0 100644 --- a/test/functional/apps/visualize/_tsvb_time_series.ts +++ b/test/functional/apps/visualize/_tsvb_time_series.ts @@ -27,8 +27,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { const browser = getService('browser'); const kibanaServer = getService('kibanaServer'); - // Failing: See https://github.com/elastic/kibana/issues/129785 - describe.skip('visual builder', function describeIndexTests() { + describe('visual builder', function describeIndexTests() { before(async () => { await security.testUser.setRoles([ 'kibana_admin', @@ -172,62 +171,62 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); describe('Clicking on the chart', () => { - it(`should create a filter`, async () => { - await visualBuilder.setMetricsGroupByTerms('machine.os.raw', { - include: 'win 7', - exclude: 'ios', - }); - await visualBuilder.clickSeriesOption(); + const act = async (visName: string, clickCoordinates: { x: number; y: number }) => { await testSubjects.click('visualizeSaveButton'); - await timeToVisualize.saveFromModal('My TSVB viz 1', { + await timeToVisualize.saveFromModal(visName, { addToDashboard: 'new', saveToLibrary: false, }); await dashboard.waitForRenderComplete(); - const el = await elasticChart.getCanvas(); await retry.try(async () => { - // click on specific coordinates - await browser - .getActions() - .move({ x: 105, y: 115, origin: el._webElement }) - .click() - .perform(); - await common.sleep(2000); - await testSubjects.click('applyFiltersPopoverButton'); - await testSubjects.missingOrFail('applyFiltersPopoverButton'); + const el = await elasticChart.getCanvas(); + + await el.clickMouseButton({ + xOffset: clickCoordinates.x, + yOffset: clickCoordinates.y, + }); + await common.sleep(1000); + await testSubjects.existOrFail('applyFiltersPopoverButton'); }); + await testSubjects.click('applyFiltersPopoverButton'); + }; + + const cleanup = async () => { + const discardDashboardPromptButton = 'discardDashboardPromptButton'; + await common.navigateToApp('dashboard'); + if (await testSubjects.exists(discardDashboardPromptButton)) { + await dashboard.clickUnsavedChangesDiscard(discardDashboardPromptButton, true); + } + }; + + afterEach(async () => { + await cleanup(); + }); + + it(`should create a filter`, async () => { + await visualBuilder.setMetricsGroupByTerms('machine.os.raw', { + include: 'win 7', + exclude: 'ios', + }); + await act('viz_1', { x: 105, y: 115 }); const hasMachineRawFilter = await filterBar.hasFilter('machine.os.raw', 'win 7'); expect(hasMachineRawFilter).to.be(true); }); it('should create a filter for series with multiple split by terms fields one of which has formatting', async () => { - const expectedFilterPills = ['0, win xp, logstash-2015.09.20']; + const expectedFilterPills = ['0, win 7']; await visualBuilder.setMetricsGroupByTerms('bytes'); await visualBuilder.setAnotherGroupByTermsField('machine.os.raw'); - await visualBuilder.setAnotherGroupByTermsField('_index'); - await visualBuilder.clickSeriesOption(); await visualBuilder.setChartType('Bar'); - await visualBuilder.setStackedType('Stacked'); await visualBuilder.clickPanelOptions('timeSeries'); await visualBuilder.setIntervalValue('1w'); - const el = await elasticChart.getCanvas(); - await el.scrollIntoViewIfNecessary(); - await browser - .getActions() - .move({ x: 100, y: 65, origin: el._webElement }) - .click() - .perform(); - - await retry.try(async () => { - await testSubjects.click('applyFiltersPopoverButton'); - await testSubjects.missingOrFail('applyFiltersPopoverButton'); - }); + await act('vis_2', { x: -100, y: 10 }); const filterPills = await filterBar.getFiltersLabel(); expect(filterPills).to.eql(expectedFilterPills); diff --git a/test/functional/page_objects/console_page.ts b/test/functional/page_objects/console_page.ts index 281c49a789ac..7aaf842f28d1 100644 --- a/test/functional/page_objects/console_page.ts +++ b/test/functional/page_objects/console_page.ts @@ -102,7 +102,6 @@ export class ConsolePageObject extends FtrService { public async enterRequest(request: string = '\nGET _search') { const textArea = await this.getEditorTextArea(); await textArea.pressKeys(request); - await textArea.pressKeys(Key.ENTER); } public async enterText(text: string) { diff --git a/test/functional/page_objects/dashboard_page.ts b/test/functional/page_objects/dashboard_page.ts index 4a77b5673e9f..cd936baa1a41 100644 --- a/test/functional/page_objects/dashboard_page.ts +++ b/test/functional/page_objects/dashboard_page.ts @@ -143,10 +143,10 @@ export class DashboardPageObject extends FtrService { await this.testSubjects.click(`edit-unsaved-${title.split(' ').join('-')}`); } - public async clickUnsavedChangesDiscard(title: string, confirmDiscard = true) { - this.log.debug(`Click Unsaved Changes Discard for `, title); - await this.testSubjects.existOrFail(`discard-unsaved-${title.split(' ').join('-')}`); - await this.testSubjects.click(`discard-unsaved-${title.split(' ').join('-')}`); + public async clickUnsavedChangesDiscard(testSubject: string, confirmDiscard = true) { + this.log.debug(`Click Unsaved Changes Discard for `, testSubject); + await this.testSubjects.existOrFail(testSubject); + await this.testSubjects.click(testSubject); if (confirmDiscard) { await this.common.clickConfirmOnModal(); } else { diff --git a/test/plugin_functional/plugins/core_plugin_a/server/plugin.ts b/test/plugin_functional/plugins/core_plugin_a/server/plugin.ts index aa86662871c2..a0f1ce279d65 100644 --- a/test/plugin_functional/plugins/core_plugin_a/server/plugin.ts +++ b/test/plugin_functional/plugins/core_plugin_a/server/plugin.ts @@ -6,15 +6,15 @@ * Side Public License, v 1. */ -import type { Plugin, CoreSetup, RequestHandlerContext } from '@kbn/core/server'; +import type { Plugin, CoreSetup, CustomRequestHandlerContext } from '@kbn/core/server'; export interface PluginAApiRequestContext { ping: () => Promise; } -interface PluginARequstHandlerContext extends RequestHandlerContext { +type PluginARequstHandlerContext = CustomRequestHandlerContext<{ pluginA: PluginAApiRequestContext; -} +}>; export class CorePluginAPlugin implements Plugin { public setup(core: CoreSetup, deps: {}) { @@ -23,7 +23,8 @@ export class CorePluginAPlugin implements Plugin { (context) => { return { ping: async () => { - const body = await context.core.elasticsearch.client.asInternalUser.ping(); + const esClient = (await context.core).elasticsearch.client; + const body = await esClient.asInternalUser.ping(); return String(body); }, }; diff --git a/test/plugin_functional/plugins/core_plugin_b/server/plugin.ts b/test/plugin_functional/plugins/core_plugin_b/server/plugin.ts index c9617eb81e8f..9a34e82b3813 100644 --- a/test/plugin_functional/plugins/core_plugin_b/server/plugin.ts +++ b/test/plugin_functional/plugins/core_plugin_b/server/plugin.ts @@ -6,20 +6,21 @@ * Side Public License, v 1. */ -import { Plugin, CoreSetup, RequestHandlerContext } from '@kbn/core/server'; +import { Plugin, CoreSetup, CustomRequestHandlerContext } from '@kbn/core/server'; import { schema } from '@kbn/config-schema'; import { PluginAApiRequestContext } from '@kbn/core-plugin-a-plugin/server'; -interface PluginBContext extends RequestHandlerContext { +type PluginBContext = CustomRequestHandlerContext<{ pluginA: PluginAApiRequestContext; -} +}>; export class CorePluginBPlugin implements Plugin { public setup(core: CoreSetup, deps: {}) { const router = core.http.createRouter(); router.get({ path: '/core_plugin_b', validate: false }, async (context, req, res) => { - if (!context.pluginA) throw new Error('pluginA is disabled'); - const response = await context.pluginA.ping(); + const pluginAContext = await context.pluginA; + if (!pluginAContext) throw new Error('pluginA is disabled'); + const response = await pluginAContext.ping(); return res.ok({ body: `Pong via plugin A: ${response}` }); }); diff --git a/test/plugin_functional/plugins/core_plugin_deprecations/server/routes.ts b/test/plugin_functional/plugins/core_plugin_deprecations/server/routes.ts index ca7c17d1c94d..aba6cd5b38e4 100644 --- a/test/plugin_functional/plugins/core_plugin_deprecations/server/routes.ts +++ b/test/plugin_functional/plugins/core_plugin_deprecations/server/routes.ts @@ -33,7 +33,7 @@ export function registerRoutes(http: HttpServiceSetup) { } if (keyId) { - const client = context.core.savedObjects.getClient(); + const client = (await context.core).savedObjects.getClient(); await client.delete('test-deprecations-plugin', keyId, { refresh: true, }); diff --git a/test/plugin_functional/plugins/core_plugin_execution_context/server/plugin.ts b/test/plugin_functional/plugins/core_plugin_execution_context/server/plugin.ts index 69001eb58b6f..d7dad3f7b3d2 100644 --- a/test/plugin_functional/plugins/core_plugin_execution_context/server/plugin.ts +++ b/test/plugin_functional/plugins/core_plugin_execution_context/server/plugin.ts @@ -20,10 +20,8 @@ export class CorePluginExecutionContext implements Plugin { }, }, async (context, request, response) => { - const { headers } = await context.core.elasticsearch.client.asCurrentUser.ping( - {}, - { meta: true } - ); + const esClient = (await context.core).elasticsearch.client; + const { headers } = await esClient.asCurrentUser.ping({}, { meta: true }); return response.ok({ body: headers || {} }); } ); diff --git a/test/plugin_functional/plugins/elasticsearch_client_plugin/server/plugin.ts b/test/plugin_functional/plugins/elasticsearch_client_plugin/server/plugin.ts index d66069aa3ab6..ba161e05aaac 100644 --- a/test/plugin_functional/plugins/elasticsearch_client_plugin/server/plugin.ts +++ b/test/plugin_functional/plugins/elasticsearch_client_plugin/server/plugin.ts @@ -10,12 +10,14 @@ import { Plugin, CoreSetup, CoreStart, ICustomClusterClient } from '@kbn/core/se export class ElasticsearchClientPlugin implements Plugin { private client?: ICustomClusterClient; + public setup(core: CoreSetup) { const router = core.http.createRouter(); router.get( { path: '/api/elasticsearch_client_plugin/context/ping', validate: false }, async (context, req, res) => { - const body = await context.core.elasticsearch.client.asInternalUser.ping(); + const esClient = (await context.core).elasticsearch.client; + const body = await esClient.asInternalUser.ping(); return res.ok({ body: JSON.stringify(body) }); } ); @@ -39,5 +41,6 @@ export class ElasticsearchClientPlugin implements Plugin { public start(core: CoreStart) { this.client = core.elasticsearch.createClient('my-custom-client-test'); } + public stop() {} } diff --git a/test/plugin_functional/plugins/ui_settings_plugin/server/plugin.ts b/test/plugin_functional/plugins/ui_settings_plugin/server/plugin.ts index fee0773a7614..daa02f90426f 100644 --- a/test/plugin_functional/plugins/ui_settings_plugin/server/plugin.ts +++ b/test/plugin_functional/plugins/ui_settings_plugin/server/plugin.ts @@ -23,13 +23,13 @@ export class UiSettingsPlugin implements Plugin { const router = core.http.createRouter(); router.get({ path: '/api/ui-settings-plugin', validate: false }, async (context, req, res) => { - const uiSettingsValue = await context.core.uiSettings.client.get( - 'ui_settings_plugin' - ); + const { uiSettings } = await context.core; + const uiSettingsValue = await uiSettings.client.get('ui_settings_plugin'); return res.ok({ body: { uiSettingsValue } }); }); } public start() {} + public stop() {} } diff --git a/test/plugin_functional/test_suites/core_plugins/rendering.ts b/test/plugin_functional/test_suites/core_plugins/rendering.ts index c75724f29490..5f124adc9296 100644 --- a/test/plugin_functional/test_suites/core_plugins/rendering.ts +++ b/test/plugin_functional/test_suites/core_plugins/rendering.ts @@ -85,11 +85,11 @@ export default function ({ getService }: PluginFunctionalProviderContext) { // Ensure that your change does not unintentionally expose any sensitive values! 'console.ui.enabled (boolean)', 'dashboard.allowByValueEmbeddables (boolean)', - 'data.autocomplete.querySuggestions.enabled (boolean)', - 'data.autocomplete.valueSuggestions.enabled (boolean)', - 'data.autocomplete.valueSuggestions.terminateAfter (duration)', - 'data.autocomplete.valueSuggestions.tiers (array)', - 'data.autocomplete.valueSuggestions.timeout (duration)', + 'unifiedSearch.autocomplete.querySuggestions.enabled (boolean)', + 'unifiedSearch.autocomplete.valueSuggestions.enabled (boolean)', + 'unifiedSearch.autocomplete.valueSuggestions.terminateAfter (duration)', + 'unifiedSearch.autocomplete.valueSuggestions.tiers (array)', + 'unifiedSearch.autocomplete.valueSuggestions.timeout (duration)', 'data.search.aggs.shardDelay.enabled (boolean)', 'enterpriseSearch.host (string)', 'home.disableWelcomeScreen (boolean)', diff --git a/x-pack/.i18nrc.json b/x-pack/.i18nrc.json index f72f8d135ff3..a632593f8b96 100644 --- a/x-pack/.i18nrc.json +++ b/x-pack/.i18nrc.json @@ -60,7 +60,7 @@ "xpack.transform": "plugins/transform", "xpack.triggersActionsUI": "plugins/triggers_actions_ui", "xpack.upgradeAssistant": "plugins/upgrade_assistant", - "xpack.uptime": ["plugins/synthetics"], + "xpack.synthetics": ["plugins/synthetics"], "xpack.ux": ["plugins/ux"], "xpack.urlDrilldown": "plugins/drilldowns/url_drilldown", "xpack.watcher": "plugins/watcher", diff --git a/x-pack/plugins/actions/server/plugin.ts b/x-pack/plugins/actions/server/plugin.ts index 43631d999c7d..6d7067628073 100644 --- a/x-pack/plugins/actions/server/plugin.ts +++ b/x-pack/plugins/actions/server/plugin.ts @@ -569,6 +569,8 @@ export class ActionsPlugin implements Plugin { if (isESOCanEncrypt !== true) { @@ -584,7 +586,7 @@ export class ActionsPlugin implements Plugin = await actionsClient.execute({ diff --git a/x-pack/plugins/actions/server/routes/get.ts b/x-pack/plugins/actions/server/routes/get.ts index 64463c49c57b..352670bd5b17 100644 --- a/x-pack/plugins/actions/server/routes/get.ts +++ b/x-pack/plugins/actions/server/routes/get.ts @@ -41,7 +41,7 @@ export const getActionRoute = ( }, router.handleLegacyErrors( verifyAccessAndContext(licenseState, async function (context, req, res) { - const actionsClient = context.actions.getActionsClient(); + const actionsClient = (await context.actions).getActionsClient(); const { id } = req.params; return res.ok({ body: rewriteBodyRes(await actionsClient.get({ id })), diff --git a/x-pack/plugins/actions/server/routes/get_all.ts b/x-pack/plugins/actions/server/routes/get_all.ts index ebb357280f85..3710b60ce510 100644 --- a/x-pack/plugins/actions/server/routes/get_all.ts +++ b/x-pack/plugins/actions/server/routes/get_all.ts @@ -34,7 +34,7 @@ export const getAllActionRoute = ( }, router.handleLegacyErrors( verifyAccessAndContext(licenseState, async function (context, req, res) { - const actionsClient = context.actions.getActionsClient(); + const actionsClient = (await context.actions).getActionsClient(); const result = await actionsClient.getAll(); return res.ok({ body: rewriteBodyRes(result), diff --git a/x-pack/plugins/actions/server/routes/legacy/create.ts b/x-pack/plugins/actions/server/routes/legacy/create.ts index 89b9f19d8263..120a7e533623 100644 --- a/x-pack/plugins/actions/server/routes/legacy/create.ts +++ b/x-pack/plugins/actions/server/routes/legacy/create.ts @@ -35,7 +35,7 @@ export const createActionRoute = ( }, router.handleLegacyErrors( verifyAccessAndContext(licenseState, async function (context, req, res) { - const actionsClient = context.actions.getActionsClient(); + const actionsClient = (await context.actions).getActionsClient(); const action = req.body; trackLegacyRouteUsage('create', usageCounter); return res.ok({ diff --git a/x-pack/plugins/actions/server/routes/legacy/delete.ts b/x-pack/plugins/actions/server/routes/legacy/delete.ts index 59392c659263..dd5f99aebd97 100644 --- a/x-pack/plugins/actions/server/routes/legacy/delete.ts +++ b/x-pack/plugins/actions/server/routes/legacy/delete.ts @@ -34,7 +34,7 @@ export const deleteActionRoute = ( if (!context.actions) { return res.badRequest({ body: 'RouteHandlerContext is not registered for actions' }); } - const actionsClient = context.actions.getActionsClient(); + const actionsClient = (await context.actions).getActionsClient(); const { id } = req.params; trackLegacyRouteUsage('delete', usageCounter); try { diff --git a/x-pack/plugins/actions/server/routes/legacy/execute.ts b/x-pack/plugins/actions/server/routes/legacy/execute.ts index 0616235cd52c..e54e5f6c5039 100644 --- a/x-pack/plugins/actions/server/routes/legacy/execute.ts +++ b/x-pack/plugins/actions/server/routes/legacy/execute.ts @@ -43,7 +43,7 @@ export const executeActionRoute = ( return res.badRequest({ body: 'RouteHandlerContext is not registered for actions' }); } - const actionsClient = context.actions.getActionsClient(); + const actionsClient = (await context.actions).getActionsClient(); const { params } = req.body; const { id } = req.params; trackLegacyRouteUsage('execute', usageCounter); diff --git a/x-pack/plugins/actions/server/routes/legacy/get.ts b/x-pack/plugins/actions/server/routes/legacy/get.ts index 7612858c9fc8..f10372023350 100644 --- a/x-pack/plugins/actions/server/routes/legacy/get.ts +++ b/x-pack/plugins/actions/server/routes/legacy/get.ts @@ -34,7 +34,7 @@ export const getActionRoute = ( if (!context.actions) { return res.badRequest({ body: 'RouteHandlerContext is not registered for actions' }); } - const actionsClient = context.actions.getActionsClient(); + const actionsClient = (await context.actions).getActionsClient(); const { id } = req.params; trackLegacyRouteUsage('get', usageCounter); return res.ok({ diff --git a/x-pack/plugins/actions/server/routes/legacy/get_all.ts b/x-pack/plugins/actions/server/routes/legacy/get_all.ts index f968f4de8594..6fc478b04d51 100644 --- a/x-pack/plugins/actions/server/routes/legacy/get_all.ts +++ b/x-pack/plugins/actions/server/routes/legacy/get_all.ts @@ -27,7 +27,7 @@ export const getAllActionRoute = ( if (!context.actions) { return res.badRequest({ body: 'RouteHandlerContext is not registered for actions' }); } - const actionsClient = context.actions.getActionsClient(); + const actionsClient = (await context.actions).getActionsClient(); const result = await actionsClient.getAll(); trackLegacyRouteUsage('getAll', usageCounter); return res.ok({ diff --git a/x-pack/plugins/actions/server/routes/legacy/list_action_types.ts b/x-pack/plugins/actions/server/routes/legacy/list_action_types.ts index 1c600b68b4e0..92213c0b9be6 100644 --- a/x-pack/plugins/actions/server/routes/legacy/list_action_types.ts +++ b/x-pack/plugins/actions/server/routes/legacy/list_action_types.ts @@ -27,7 +27,7 @@ export const listActionTypesRoute = ( if (!context.actions) { return res.badRequest({ body: 'RouteHandlerContext is not registered for actions' }); } - const actionsClient = context.actions.getActionsClient(); + const actionsClient = (await context.actions).getActionsClient(); trackLegacyRouteUsage('listActionTypes', usageCounter); return res.ok({ body: await actionsClient.listTypes(), diff --git a/x-pack/plugins/actions/server/routes/legacy/update.ts b/x-pack/plugins/actions/server/routes/legacy/update.ts index 28edcfd33689..a4e441d5d984 100644 --- a/x-pack/plugins/actions/server/routes/legacy/update.ts +++ b/x-pack/plugins/actions/server/routes/legacy/update.ts @@ -41,7 +41,7 @@ export const updateActionRoute = ( if (!context.actions) { return res.badRequest({ body: 'RouteHandlerContext is not registered for actions' }); } - const actionsClient = context.actions.getActionsClient(); + const actionsClient = (await context.actions).getActionsClient(); const { id } = req.params; const { name, config, secrets } = req.body; trackLegacyRouteUsage('update', usageCounter); diff --git a/x-pack/plugins/actions/server/routes/update.ts b/x-pack/plugins/actions/server/routes/update.ts index 3f1ceb0a0096..a8892c814fc6 100644 --- a/x-pack/plugins/actions/server/routes/update.ts +++ b/x-pack/plugins/actions/server/routes/update.ts @@ -48,7 +48,7 @@ export const updateActionRoute = ( }, router.handleLegacyErrors( verifyAccessAndContext(licenseState, async function (context, req, res) { - const actionsClient = context.actions.getActionsClient(); + const actionsClient = (await context.actions).getActionsClient(); const { id } = req.params; const { name, config, secrets } = req.body; diff --git a/x-pack/plugins/actions/server/types.ts b/x-pack/plugins/actions/server/types.ts index b13df8dfe246..9cb4f1aec63e 100644 --- a/x-pack/plugins/actions/server/types.ts +++ b/x-pack/plugins/actions/server/types.ts @@ -12,7 +12,7 @@ import { SavedObjectsClientContract, SavedObjectAttributes, ElasticsearchClient, - RequestHandlerContext, + CustomRequestHandlerContext, SavedObjectReference, } from '@kbn/core/server'; import { ActionTypeRegistry } from './action_type_registry'; @@ -21,6 +21,7 @@ import { ActionsClient } from './actions_client'; import { ActionTypeExecutorResult } from '../common'; import { TaskInfo } from './lib/action_executor'; import { ConnectorTokenClient } from './builtin_action_types/lib/connector_token_client'; + export type { ActionTypeExecutorResult } from '../common'; export type { GetFieldsByIssueTypeResponse as JiraGetFieldsResponse } from './builtin_action_types/jira/types'; export type { GetCommonFieldsResponse as ServiceNowGetFieldsResponse } from './builtin_action_types/servicenow/types'; @@ -46,9 +47,9 @@ export interface ActionsApiRequestHandlerContext { listTypes: ActionTypeRegistry['list']; } -export interface ActionsRequestHandlerContext extends RequestHandlerContext { +export type ActionsRequestHandlerContext = CustomRequestHandlerContext<{ actions: ActionsApiRequestHandlerContext; -} +}>; export interface ActionsPlugin { setup: PluginSetupContract; @@ -97,6 +98,7 @@ interface ValidatorType { export interface ActionValidationService { isHostnameAllowed(hostname: string): boolean; + isUriAllowed(uri: string): boolean; } @@ -116,11 +118,13 @@ export interface ActionType< secrets?: ValidatorType; connector?: (config: Config, secrets: Secrets) => string | null; }; + renderParameterTemplates?( params: Params, variables: Record, actionId?: string ): Params; + executor: ExecutorType; } @@ -146,6 +150,7 @@ interface PersistedActionTaskExecutorParams { spaceId: string; actionTaskParamsId: string; } + interface EphemeralActionTaskExecutorParams { spaceId: string; taskParams: ActionTaskParams; diff --git a/x-pack/plugins/alerting/server/routes/aggregate_rules.ts b/x-pack/plugins/alerting/server/routes/aggregate_rules.ts index 5bba2b714d4d..312def72dd65 100644 --- a/x-pack/plugins/alerting/server/routes/aggregate_rules.ts +++ b/x-pack/plugins/alerting/server/routes/aggregate_rules.ts @@ -73,7 +73,7 @@ export const aggregateRulesRoute = ( }, router.handleLegacyErrors( verifyAccessAndContext(licenseState, async function (context, req, res) { - const rulesClient = context.alerting.getRulesClient(); + const rulesClient = (await context.alerting).getRulesClient(); const options = rewriteQueryReq({ ...req.query, has_reference: req.query.has_reference || undefined, diff --git a/x-pack/plugins/alerting/server/routes/create_rule.ts b/x-pack/plugins/alerting/server/routes/create_rule.ts index 5278f748f0ce..cf044c94f252 100644 --- a/x-pack/plugins/alerting/server/routes/create_rule.ts +++ b/x-pack/plugins/alerting/server/routes/create_rule.ts @@ -110,7 +110,7 @@ export const createRuleRoute = ({ router, licenseState, usageCounter }: RouteOpt handleDisabledApiKeysError( router.handleLegacyErrors( verifyAccessAndContext(licenseState, async function (context, req, res) { - const rulesClient = context.alerting.getRulesClient(); + const rulesClient = (await context.alerting).getRulesClient(); const rule = req.body; const params = req.params; diff --git a/x-pack/plugins/alerting/server/routes/delete_rule.ts b/x-pack/plugins/alerting/server/routes/delete_rule.ts index 278bd031a8d6..e4ccd491046d 100644 --- a/x-pack/plugins/alerting/server/routes/delete_rule.ts +++ b/x-pack/plugins/alerting/server/routes/delete_rule.ts @@ -28,7 +28,7 @@ export const deleteRuleRoute = ( }, router.handleLegacyErrors( verifyAccessAndContext(licenseState, async function (context, req, res) { - const rulesClient = context.alerting.getRulesClient(); + const rulesClient = (await context.alerting).getRulesClient(); const { id } = req.params; await rulesClient.delete({ id }); return res.noContent(); diff --git a/x-pack/plugins/alerting/server/routes/disable_rule.ts b/x-pack/plugins/alerting/server/routes/disable_rule.ts index 6e6700c3ece4..99795e7ee2bf 100644 --- a/x-pack/plugins/alerting/server/routes/disable_rule.ts +++ b/x-pack/plugins/alerting/server/routes/disable_rule.ts @@ -28,7 +28,7 @@ export const disableRuleRoute = ( }, router.handleLegacyErrors( verifyAccessAndContext(licenseState, async function (context, req, res) { - const rulesClient = context.alerting.getRulesClient(); + const rulesClient = (await context.alerting).getRulesClient(); const { id } = req.params; try { await rulesClient.disable({ id }); diff --git a/x-pack/plugins/alerting/server/routes/enable_rule.ts b/x-pack/plugins/alerting/server/routes/enable_rule.ts index f3b3c5bfbefd..84a4086cd245 100644 --- a/x-pack/plugins/alerting/server/routes/enable_rule.ts +++ b/x-pack/plugins/alerting/server/routes/enable_rule.ts @@ -28,7 +28,7 @@ export const enableRuleRoute = ( }, router.handleLegacyErrors( verifyAccessAndContext(licenseState, async function (context, req, res) { - const rulesClient = context.alerting.getRulesClient(); + const rulesClient = (await context.alerting).getRulesClient(); const { id } = req.params; try { await rulesClient.enable({ id }); diff --git a/x-pack/plugins/alerting/server/routes/find_rules.ts b/x-pack/plugins/alerting/server/routes/find_rules.ts index 2c5819f7b711..ef8b8b29057c 100644 --- a/x-pack/plugins/alerting/server/routes/find_rules.ts +++ b/x-pack/plugins/alerting/server/routes/find_rules.ts @@ -140,7 +140,7 @@ const buildFindRulesRoute = ({ }, router.handleLegacyErrors( verifyAccessAndContext(licenseState, async function (context, req, res) { - const rulesClient = context.alerting.getRulesClient(); + const rulesClient = (await context.alerting).getRulesClient(); trackLegacyTerminology( [req.query.search, req.query.search_fields, req.query.sort_field].filter( diff --git a/x-pack/plugins/alerting/server/routes/get_rule.ts b/x-pack/plugins/alerting/server/routes/get_rule.ts index 189401f03815..f4414b0364dc 100644 --- a/x-pack/plugins/alerting/server/routes/get_rule.ts +++ b/x-pack/plugins/alerting/server/routes/get_rule.ts @@ -85,7 +85,7 @@ const buildGetRuleRoute = ({ }, router.handleLegacyErrors( verifyAccessAndContext(licenseState, async function (context, req, res) { - const rulesClient = context.alerting.getRulesClient(); + const rulesClient = (await context.alerting).getRulesClient(); const { id } = req.params; const rule = await rulesClient.get({ id, excludeFromPublicApi }); return res.ok({ diff --git a/x-pack/plugins/alerting/server/routes/get_rule_alert_summary.ts b/x-pack/plugins/alerting/server/routes/get_rule_alert_summary.ts index 033c0f178cef..c0d31d1ccbfa 100644 --- a/x-pack/plugins/alerting/server/routes/get_rule_alert_summary.ts +++ b/x-pack/plugins/alerting/server/routes/get_rule_alert_summary.ts @@ -72,7 +72,7 @@ export const getRuleAlertSummaryRoute = ( }, router.handleLegacyErrors( verifyAccessAndContext(licenseState, async function (context, req, res) { - const rulesClient = context.alerting.getRulesClient(); + const rulesClient = (await context.alerting).getRulesClient(); const { id } = req.params; const summary = await rulesClient.getAlertSummary(rewriteReq({ id, ...req.query })); return res.ok({ body: rewriteBodyRes(summary) }); diff --git a/x-pack/plugins/alerting/server/routes/get_rule_execution_log.ts b/x-pack/plugins/alerting/server/routes/get_rule_execution_log.ts index 6f07c8daa14e..650bdd83a0a8 100644 --- a/x-pack/plugins/alerting/server/routes/get_rule_execution_log.ts +++ b/x-pack/plugins/alerting/server/routes/get_rule_execution_log.ts @@ -67,7 +67,7 @@ export const getRuleExecutionLogRoute = ( }, router.handleLegacyErrors( verifyAccessAndContext(licenseState, async function (context, req, res) { - const rulesClient = context.alerting.getRulesClient(); + const rulesClient = (await context.alerting).getRulesClient(); const { id } = req.params; return res.ok({ body: await rulesClient.getExecutionLogForRule(rewriteReq({ id, ...req.query })), diff --git a/x-pack/plugins/alerting/server/routes/get_rule_state.ts b/x-pack/plugins/alerting/server/routes/get_rule_state.ts index 5f9bee59c4e7..50ad1f776110 100644 --- a/x-pack/plugins/alerting/server/routes/get_rule_state.ts +++ b/x-pack/plugins/alerting/server/routes/get_rule_state.ts @@ -44,7 +44,7 @@ export const getRuleStateRoute = ( }, router.handleLegacyErrors( verifyAccessAndContext(licenseState, async function (context, req, res) { - const rulesClient = context.alerting.getRulesClient(); + const rulesClient = (await context.alerting).getRulesClient(); const { id } = req.params; const state = await rulesClient.getAlertState({ id }); return state ? res.ok({ body: rewriteBodyRes(state) }) : res.noContent(); diff --git a/x-pack/plugins/alerting/server/routes/health.ts b/x-pack/plugins/alerting/server/routes/health.ts index 4d9fcde89628..4d3934911221 100644 --- a/x-pack/plugins/alerting/server/routes/health.ts +++ b/x-pack/plugins/alerting/server/routes/health.ts @@ -52,15 +52,16 @@ export const healthRoute = ( router.handleLegacyErrors( verifyAccessAndContext(licenseState, async function (context, req, res) { try { + const alertingContext = await context.alerting; // Verify that user has access to at least one rule type - const ruleTypes = Array.from(await context.alerting.getRulesClient().listAlertTypes()); + const ruleTypes = Array.from(await alertingContext.getRulesClient().listAlertTypes()); if (ruleTypes.length > 0) { - const alertingFrameworkHealth = await context.alerting.getFrameworkHealth(); + const alertingFrameworkHealth = await alertingContext.getFrameworkHealth(); const securityHealth = await getSecurityHealth( async () => (licenseState ? licenseState.getIsSecurityEnabled() : null), async () => encryptedSavedObjects.canEncrypt, - context.alerting.areApiKeysEnabled + alertingContext.areApiKeysEnabled ); const frameworkHealth: AlertingFrameworkHealth = { diff --git a/x-pack/plugins/alerting/server/routes/legacy/aggregate.ts b/x-pack/plugins/alerting/server/routes/legacy/aggregate.ts index 67bb1a416d1c..ba520cc40126 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/aggregate.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/aggregate.ts @@ -53,7 +53,7 @@ export const aggregateAlertRoute = ( if (!context.alerting) { return res.badRequest({ body: 'RouteHandlerContext is not registered for alerting' }); } - const rulesClient = context.alerting.getRulesClient(); + const rulesClient = (await context.alerting).getRulesClient(); trackLegacyRouteUsage('aggregate', usageCounter); trackLegacyTerminology( diff --git a/x-pack/plugins/alerting/server/routes/legacy/create.ts b/x-pack/plugins/alerting/server/routes/legacy/create.ts index fb56279eaabc..e7583033ae4b 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/create.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/create.ts @@ -64,7 +64,7 @@ export const createAlertRoute = ({ router, licenseState, usageCounter }: RouteOp if (!context.alerting) { return res.badRequest({ body: 'RouteHandlerContext is not registered for alerting' }); } - const rulesClient = context.alerting.getRulesClient(); + const rulesClient = (await context.alerting).getRulesClient(); const alert = req.body; const params = req.params; const notifyWhen = alert?.notifyWhen ? (alert.notifyWhen as RuleNotifyWhenType) : null; diff --git a/x-pack/plugins/alerting/server/routes/legacy/delete.ts b/x-pack/plugins/alerting/server/routes/legacy/delete.ts index 4e29ded69518..4fcf85678db5 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/delete.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/delete.ts @@ -35,7 +35,7 @@ export const deleteAlertRoute = ( return res.badRequest({ body: 'RouteHandlerContext is not registered for alerting' }); } trackLegacyRouteUsage('delete', usageCounter); - const rulesClient = context.alerting.getRulesClient(); + const rulesClient = (await context.alerting).getRulesClient(); const { id } = req.params; await rulesClient.delete({ id }); return res.noContent(); diff --git a/x-pack/plugins/alerting/server/routes/legacy/disable.ts b/x-pack/plugins/alerting/server/routes/legacy/disable.ts index b9ad5765e977..5572220eb217 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/disable.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/disable.ts @@ -36,7 +36,7 @@ export const disableAlertRoute = ( return res.badRequest({ body: 'RouteHandlerContext is not registered for alerting' }); } trackLegacyRouteUsage('disable', usageCounter); - const rulesClient = context.alerting.getRulesClient(); + const rulesClient = (await context.alerting).getRulesClient(); const { id } = req.params; try { await rulesClient.disable({ id }); diff --git a/x-pack/plugins/alerting/server/routes/legacy/enable.ts b/x-pack/plugins/alerting/server/routes/legacy/enable.ts index 6835be0631cb..eb86b5db65da 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/enable.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/enable.ts @@ -38,7 +38,7 @@ export const enableAlertRoute = ( return res.badRequest({ body: 'RouteHandlerContext is not registered for alerting' }); } trackLegacyRouteUsage('enable', usageCounter); - const rulesClient = context.alerting.getRulesClient(); + const rulesClient = (await context.alerting).getRulesClient(); const { id } = req.params; try { await rulesClient.enable({ id }); diff --git a/x-pack/plugins/alerting/server/routes/legacy/find.ts b/x-pack/plugins/alerting/server/routes/legacy/find.ts index e6d57572a223..9a33f5b2dc5f 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/find.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/find.ts @@ -66,7 +66,7 @@ export const findAlertRoute = ( ) as string[], usageCounter ); - const rulesClient = context.alerting.getRulesClient(); + const rulesClient = (await context.alerting).getRulesClient(); const query = req.query; const renameMap = { diff --git a/x-pack/plugins/alerting/server/routes/legacy/get.ts b/x-pack/plugins/alerting/server/routes/legacy/get.ts index 82b56030de4c..62fdde550714 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/get.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/get.ts @@ -35,7 +35,7 @@ export const getAlertRoute = ( return res.badRequest({ body: 'RouteHandlerContext is not registered for alerting' }); } trackLegacyRouteUsage('get', usageCounter); - const rulesClient = context.alerting.getRulesClient(); + const rulesClient = (await context.alerting).getRulesClient(); const { id } = req.params; return res.ok({ body: await rulesClient.get({ id, excludeFromPublicApi: true }), diff --git a/x-pack/plugins/alerting/server/routes/legacy/get_alert_instance_summary.ts b/x-pack/plugins/alerting/server/routes/legacy/get_alert_instance_summary.ts index 7d467aed1b3f..c33aea8060d7 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/get_alert_instance_summary.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/get_alert_instance_summary.ts @@ -46,7 +46,7 @@ export const getAlertInstanceSummaryRoute = ( return res.badRequest({ body: 'RouteHandlerContext is not registered for alerting' }); } trackLegacyRouteUsage('instanceSummary', usageCounter); - const rulesClient = context.alerting.getRulesClient(); + const rulesClient = (await context.alerting).getRulesClient(); const { id } = req.params; const { dateStart } = req.query; const summary = await rulesClient.getAlertSummary({ id, dateStart }); diff --git a/x-pack/plugins/alerting/server/routes/legacy/get_alert_state.ts b/x-pack/plugins/alerting/server/routes/legacy/get_alert_state.ts index 49a0daf5ea18..86a56403b39a 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/get_alert_state.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/get_alert_state.ts @@ -35,7 +35,7 @@ export const getAlertStateRoute = ( return res.badRequest({ body: 'RouteHandlerContext is not registered for alerting' }); } trackLegacyRouteUsage('state', usageCounter); - const rulesClient = context.alerting.getRulesClient(); + const rulesClient = (await context.alerting).getRulesClient(); const { id } = req.params; const state = await rulesClient.getAlertState({ id }); return state ? res.ok({ body: state }) : res.noContent(); diff --git a/x-pack/plugins/alerting/server/routes/legacy/health.ts b/x-pack/plugins/alerting/server/routes/legacy/health.ts index e9f770b437ca..02b8c8279b81 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/health.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/health.ts @@ -32,15 +32,16 @@ export function healthRoute( } trackLegacyRouteUsage('health', usageCounter); try { + const alertingContext = await context.alerting; // Verify that user has access to at least one rule type - const ruleTypes = Array.from(await context.alerting.getRulesClient().listAlertTypes()); + const ruleTypes = Array.from(await alertingContext.getRulesClient().listAlertTypes()); if (ruleTypes.length > 0) { - const alertingFrameworkHealth = await context.alerting.getFrameworkHealth(); + const alertingFrameworkHealth = await alertingContext.getFrameworkHealth(); const securityHealth = await getSecurityHealth( async () => (licenseState ? licenseState.getIsSecurityEnabled() : null), async () => encryptedSavedObjects.canEncrypt, - context.alerting.areApiKeysEnabled + alertingContext.areApiKeysEnabled ); const frameworkHealth: AlertingFrameworkHealth = { diff --git a/x-pack/plugins/alerting/server/routes/legacy/list_alert_types.ts b/x-pack/plugins/alerting/server/routes/legacy/list_alert_types.ts index 5c0ef90ccc5e..8cfef8af4624 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/list_alert_types.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/list_alert_types.ts @@ -27,8 +27,9 @@ export const listAlertTypesRoute = ( return res.badRequest({ body: 'RouteHandlerContext is not registered for alerting' }); } trackLegacyRouteUsage('listAlertTypes', usageCounter); + const alertingContext = await context.alerting; return res.ok({ - body: Array.from(await context.alerting.getRulesClient().listAlertTypes()), + body: Array.from(await alertingContext.getRulesClient().listAlertTypes()), }); }) ); diff --git a/x-pack/plugins/alerting/server/routes/legacy/mute_all.ts b/x-pack/plugins/alerting/server/routes/legacy/mute_all.ts index 2b16b332c9cb..dcbc0c19dbe3 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/mute_all.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/mute_all.ts @@ -36,7 +36,7 @@ export const muteAllAlertRoute = ( return res.badRequest({ body: 'RouteHandlerContext is not registered for alerting' }); } trackLegacyRouteUsage('muteAll', usageCounter); - const rulesClient = context.alerting.getRulesClient(); + const rulesClient = (await context.alerting).getRulesClient(); const { id } = req.params; try { await rulesClient.muteAll({ id }); diff --git a/x-pack/plugins/alerting/server/routes/legacy/mute_instance.ts b/x-pack/plugins/alerting/server/routes/legacy/mute_instance.ts index 50c4a3de9589..f6c6a28ecc86 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/mute_instance.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/mute_instance.ts @@ -41,7 +41,7 @@ export const muteAlertInstanceRoute = ( trackLegacyRouteUsage('muteInstance', usageCounter); - const rulesClient = context.alerting.getRulesClient(); + const rulesClient = (await context.alerting).getRulesClient(); const renameMap = { alert_id: 'alertId', diff --git a/x-pack/plugins/alerting/server/routes/legacy/unmute_all.ts b/x-pack/plugins/alerting/server/routes/legacy/unmute_all.ts index 34297624b45d..31f82849ea1d 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/unmute_all.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/unmute_all.ts @@ -36,7 +36,7 @@ export const unmuteAllAlertRoute = ( return res.badRequest({ body: 'RouteHandlerContext is not registered for alerting' }); } trackLegacyRouteUsage('unmuteAll', usageCounter); - const rulesClient = context.alerting.getRulesClient(); + const rulesClient = (await context.alerting).getRulesClient(); const { id } = req.params; try { await rulesClient.unmuteAll({ id }); diff --git a/x-pack/plugins/alerting/server/routes/legacy/unmute_instance.ts b/x-pack/plugins/alerting/server/routes/legacy/unmute_instance.ts index 8f3b1f241395..4d5b9b02da5f 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/unmute_instance.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/unmute_instance.ts @@ -37,7 +37,7 @@ export const unmuteAlertInstanceRoute = ( return res.badRequest({ body: 'RouteHandlerContext is not registered for alerting' }); } trackLegacyRouteUsage('unmuteInstance', usageCounter); - const rulesClient = context.alerting.getRulesClient(); + const rulesClient = (await context.alerting).getRulesClient(); const { alertId, alertInstanceId } = req.params; try { await rulesClient.unmuteInstance({ alertId, alertInstanceId }); diff --git a/x-pack/plugins/alerting/server/routes/legacy/update.ts b/x-pack/plugins/alerting/server/routes/legacy/update.ts index 415d2f0af4a6..07bde524076c 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/update.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/update.ts @@ -64,7 +64,7 @@ export const updateAlertRoute = ( return res.badRequest({ body: 'RouteHandlerContext is not registered for alerting' }); } trackLegacyRouteUsage('update', usageCounter); - const rulesClient = context.alerting.getRulesClient(); + const rulesClient = (await context.alerting).getRulesClient(); const { id } = req.params; const { name, actions, params, schedule, tags, throttle, notifyWhen } = req.body; try { diff --git a/x-pack/plugins/alerting/server/routes/legacy/update_api_key.ts b/x-pack/plugins/alerting/server/routes/legacy/update_api_key.ts index 3c288219812c..f0f7716b0077 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/update_api_key.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/update_api_key.ts @@ -38,7 +38,7 @@ export const updateApiKeyRoute = ( return res.badRequest({ body: 'RouteHandlerContext is not registered for alerting' }); } trackLegacyRouteUsage('updateApiKey', usageCounter); - const rulesClient = context.alerting.getRulesClient(); + const rulesClient = (await context.alerting).getRulesClient(); const { id } = req.params; try { await rulesClient.updateApiKey({ id }); diff --git a/x-pack/plugins/alerting/server/routes/mute_alert.ts b/x-pack/plugins/alerting/server/routes/mute_alert.ts index a90dc77e5fb6..22d9a1670dde 100644 --- a/x-pack/plugins/alerting/server/routes/mute_alert.ts +++ b/x-pack/plugins/alerting/server/routes/mute_alert.ts @@ -38,7 +38,7 @@ export const muteAlertRoute = ( }, router.handleLegacyErrors( verifyAccessAndContext(licenseState, async function (context, req, res) { - const rulesClient = context.alerting.getRulesClient(); + const rulesClient = (await context.alerting).getRulesClient(); const params = rewriteParamsReq(req.params); try { await rulesClient.muteInstance(params); diff --git a/x-pack/plugins/alerting/server/routes/mute_all_rule.ts b/x-pack/plugins/alerting/server/routes/mute_all_rule.ts index e9d64ce3e30f..41b561db0b2f 100644 --- a/x-pack/plugins/alerting/server/routes/mute_all_rule.ts +++ b/x-pack/plugins/alerting/server/routes/mute_all_rule.ts @@ -31,7 +31,7 @@ export const muteAllRuleRoute = ( }, router.handleLegacyErrors( verifyAccessAndContext(licenseState, async function (context, req, res) { - const rulesClient = context.alerting.getRulesClient(); + const rulesClient = (await context.alerting).getRulesClient(); const { id } = req.params; trackDeprecatedRouteUsage('muteAll', usageCounter); try { diff --git a/x-pack/plugins/alerting/server/routes/resolve_rule.ts b/x-pack/plugins/alerting/server/routes/resolve_rule.ts index 02c70c555fc0..cde747f9272f 100644 --- a/x-pack/plugins/alerting/server/routes/resolve_rule.ts +++ b/x-pack/plugins/alerting/server/routes/resolve_rule.ts @@ -73,7 +73,7 @@ export const resolveRuleRoute = ( }, router.handleLegacyErrors( verifyAccessAndContext(licenseState, async function (context, req, res) { - const rulesClient = context.alerting.getRulesClient(); + const rulesClient = (await context.alerting).getRulesClient(); const { id } = req.params; const rule = await rulesClient.resolve({ id }); return res.ok({ diff --git a/x-pack/plugins/alerting/server/routes/rule_types.ts b/x-pack/plugins/alerting/server/routes/rule_types.ts index f78a0ef3ee89..5d6c9e9a3f0f 100644 --- a/x-pack/plugins/alerting/server/routes/rule_types.ts +++ b/x-pack/plugins/alerting/server/routes/rule_types.ts @@ -54,7 +54,8 @@ export const ruleTypesRoute = ( }, router.handleLegacyErrors( verifyAccessAndContext(licenseState, async function (context, req, res) { - const ruleTypes = Array.from(await context.alerting.getRulesClient().listAlertTypes()); + const rulesClient = (await context.alerting).getRulesClient(); + const ruleTypes = Array.from(await rulesClient.listAlertTypes()); return res.ok({ body: rewriteBodyRes(ruleTypes), }); diff --git a/x-pack/plugins/alerting/server/routes/snooze_rule.ts b/x-pack/plugins/alerting/server/routes/snooze_rule.ts index ed717de973c9..a408a4d3175d 100644 --- a/x-pack/plugins/alerting/server/routes/snooze_rule.ts +++ b/x-pack/plugins/alerting/server/routes/snooze_rule.ts @@ -44,7 +44,7 @@ export const snoozeRuleRoute = ( }, router.handleLegacyErrors( verifyAccessAndContext(licenseState, async function (context, req, res) { - const rulesClient = context.alerting.getRulesClient(); + const rulesClient = (await context.alerting).getRulesClient(); const params = req.params; const body = rewriteBodyReq(req.body); try { diff --git a/x-pack/plugins/alerting/server/routes/unmute_alert.ts b/x-pack/plugins/alerting/server/routes/unmute_alert.ts index 6b02674de026..1e4191745f62 100644 --- a/x-pack/plugins/alerting/server/routes/unmute_alert.ts +++ b/x-pack/plugins/alerting/server/routes/unmute_alert.ts @@ -38,7 +38,7 @@ export const unmuteAlertRoute = ( }, router.handleLegacyErrors( verifyAccessAndContext(licenseState, async function (context, req, res) { - const rulesClient = context.alerting.getRulesClient(); + const rulesClient = (await context.alerting).getRulesClient(); const params = rewriteParamsReq(req.params); try { await rulesClient.unmuteInstance(params); diff --git a/x-pack/plugins/alerting/server/routes/unmute_all_rule.ts b/x-pack/plugins/alerting/server/routes/unmute_all_rule.ts index bead959a6d7d..4e102f96cf38 100644 --- a/x-pack/plugins/alerting/server/routes/unmute_all_rule.ts +++ b/x-pack/plugins/alerting/server/routes/unmute_all_rule.ts @@ -28,7 +28,7 @@ export const unmuteAllRuleRoute = ( }, router.handleLegacyErrors( verifyAccessAndContext(licenseState, async function (context, req, res) { - const rulesClient = context.alerting.getRulesClient(); + const rulesClient = (await context.alerting).getRulesClient(); const { id } = req.params; try { await rulesClient.unmuteAll({ id }); diff --git a/x-pack/plugins/alerting/server/routes/unsnooze_rule.ts b/x-pack/plugins/alerting/server/routes/unsnooze_rule.ts index f0c1a9a12ac5..b91fb24bea56 100644 --- a/x-pack/plugins/alerting/server/routes/unsnooze_rule.ts +++ b/x-pack/plugins/alerting/server/routes/unsnooze_rule.ts @@ -28,7 +28,7 @@ export const unsnoozeRuleRoute = ( }, router.handleLegacyErrors( verifyAccessAndContext(licenseState, async function (context, req, res) { - const rulesClient = context.alerting.getRulesClient(); + const rulesClient = (await context.alerting).getRulesClient(); const params = req.params; try { await rulesClient.unsnooze({ ...params }); diff --git a/x-pack/plugins/alerting/server/routes/update_rule.ts b/x-pack/plugins/alerting/server/routes/update_rule.ts index f58e61b5d1ac..d2130e1f3354 100644 --- a/x-pack/plugins/alerting/server/routes/update_rule.ts +++ b/x-pack/plugins/alerting/server/routes/update_rule.ts @@ -119,7 +119,7 @@ export const updateRuleRoute = ( handleDisabledApiKeysError( router.handleLegacyErrors( verifyAccessAndContext(licenseState, async function (context, req, res) { - const rulesClient = context.alerting.getRulesClient(); + const rulesClient = (await context.alerting).getRulesClient(); const { id } = req.params; const rule = req.body; try { diff --git a/x-pack/plugins/alerting/server/routes/update_rule_api_key.ts b/x-pack/plugins/alerting/server/routes/update_rule_api_key.ts index af10d97bc2a9..4e99b54e76af 100644 --- a/x-pack/plugins/alerting/server/routes/update_rule_api_key.ts +++ b/x-pack/plugins/alerting/server/routes/update_rule_api_key.ts @@ -28,7 +28,7 @@ export const updateRuleApiKeyRoute = ( }, router.handleLegacyErrors( verifyAccessAndContext(licenseState, async function (context, req, res) { - const rulesClient = context.alerting.getRulesClient(); + const rulesClient = (await context.alerting).getRulesClient(); const { id } = req.params; try { await rulesClient.updateApiKey({ id }); diff --git a/x-pack/plugins/alerting/server/types.ts b/x-pack/plugins/alerting/server/types.ts index 7469bdfd9827..32c6e41c6326 100644 --- a/x-pack/plugins/alerting/server/types.ts +++ b/x-pack/plugins/alerting/server/types.ts @@ -7,18 +7,16 @@ import type { IRouter, - RequestHandlerContext, + CustomRequestHandlerContext, SavedObjectReference, IUiSettingsClient, -} from '@kbn/core/server'; -import type { PublicMethodsOf } from '@kbn/utility-types'; -import { ISearchStartSearchSource } from '@kbn/data-plugin/common'; -import { LicenseType } from '@kbn/licensing-plugin/server'; -import { IScopedClusterClient, SavedObjectAttributes, SavedObjectsClientContract, } from '@kbn/core/server'; +import type { PublicMethodsOf } from '@kbn/utility-types'; +import { ISearchStartSearchSource } from '@kbn/data-plugin/common'; +import { LicenseType } from '@kbn/licensing-plugin/server'; import { AlertFactoryDoneUtils, PublicAlert } from './alert'; import { RuleTypeRegistry as OrigruleTypeRegistry } from './rule_type_registry'; import { PluginSetupContract, PluginStartContract } from './plugin'; @@ -60,9 +58,9 @@ export interface AlertingApiRequestHandlerContext { /** * @internal */ -export interface AlertingRequestHandlerContext extends RequestHandlerContext { +export type AlertingRequestHandlerContext = CustomRequestHandlerContext<{ alerting: AlertingApiRequestHandlerContext; -} +}>; /** * @internal diff --git a/x-pack/plugins/apm/kibana.json b/x-pack/plugins/apm/kibana.json index 3917fd601d75..9bb1c52b52d7 100644 --- a/x-pack/plugins/apm/kibana.json +++ b/x-pack/plugins/apm/kibana.json @@ -16,7 +16,8 @@ "licensing", "observability", "ruleRegistry", - "triggersActionsUi" + "triggersActionsUi", + "unifiedSearch" ], "optionalPlugins": [ "actions", diff --git a/x-pack/plugins/apm/public/components/shared/kuery_bar/index.tsx b/x-pack/plugins/apm/public/components/shared/kuery_bar/index.tsx index 93fee4bf0686..753d3b6460ea 100644 --- a/x-pack/plugins/apm/public/components/shared/kuery_bar/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/kuery_bar/index.tsx @@ -10,8 +10,9 @@ import { i18n } from '@kbn/i18n'; import { uniqueId } from 'lodash'; import React, { useState } from 'react'; import { useHistory, useLocation } from 'react-router-dom'; +import { QuerySuggestion } from '@kbn/unified-search-plugin/public'; import { DataView } from '@kbn/data-plugin/common'; -import { esKuery, QuerySuggestion } from '@kbn/data-plugin/public'; +import { esKuery } from '@kbn/data-plugin/public'; import { useApmPluginContext } from '../../../context/apm_plugin/use_apm_plugin_context'; import { useLegacyUrlParams } from '../../../context/url_params_context/use_url_params'; import { useApmParams } from '../../../hooks/use_apm_params'; @@ -55,7 +56,7 @@ export function KueryBar(props: { }); const { urlParams } = useLegacyUrlParams(); const location = useLocation(); - const { data } = useApmPluginContext().plugins; + const { unifiedSearch } = useApmPluginContext().plugins; let currentRequestCheck; @@ -103,7 +104,7 @@ export function KueryBar(props: { try { const suggestions = ( - (await data.autocomplete.getQuerySuggestions({ + (await unifiedSearch.autocomplete.getQuerySuggestions({ language: 'kuery', indexPatterns: [dataView], boolFilter: diff --git a/x-pack/plugins/apm/public/plugin.ts b/x-pack/plugins/apm/public/plugin.ts index 591b2322af0d..627196a9a4bd 100644 --- a/x-pack/plugins/apm/public/plugin.ts +++ b/x-pack/plugins/apm/public/plugin.ts @@ -4,6 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ + import { i18n } from '@kbn/i18n'; import { from } from 'rxjs'; import { map } from 'rxjs/operators'; @@ -17,9 +18,10 @@ import { PluginInitializerContext, } from '@kbn/core/public'; import type { - DataPublicPluginSetup, DataPublicPluginStart, + DataPublicPluginSetup, } from '@kbn/data-plugin/public'; +import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; import type { EmbeddableStart } from '@kbn/embeddable-plugin/public'; import type { HomePublicPluginSetup } from '@kbn/home-plugin/public'; import { Start as InspectorPluginStart } from '@kbn/inspector-plugin/public'; @@ -63,6 +65,7 @@ export type ApmPluginStart = void; export interface ApmPluginSetupDeps { alerting?: AlertingPluginPublicSetup; data: DataPublicPluginSetup; + unifiedSearch: UnifiedSearchPublicPluginStart; features: FeaturesPluginSetup; home?: HomePublicPluginSetup; licensing: LicensingPluginSetup; diff --git a/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_internal_es_client/index.ts b/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_internal_es_client/index.ts index f494a39956f2..522fd5c078af 100644 --- a/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_internal_es_client/index.ts +++ b/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_internal_es_client/index.ts @@ -21,14 +21,16 @@ import { cancelEsRequestOnAbort } from '../cancel_es_request_on_abort'; export type APMIndexDocumentParams = estypes.IndexRequest; -export type APMInternalClient = ReturnType; +export type APMInternalClient = Awaited< + ReturnType +>; -export function createInternalESClient({ +export async function createInternalESClient({ context, debug, request, }: Pick & { debug: boolean }) { - const { asInternalUser } = context.core.elasticsearch.client; + const { asInternalUser } = (await context.core).elasticsearch.client; function callEs( operationName: string, diff --git a/x-pack/plugins/apm/server/lib/helpers/setup_request.ts b/x-pack/plugins/apm/server/lib/helpers/setup_request.ts index a3eb942111a4..2701d754350f 100644 --- a/x-pack/plugins/apm/server/lib/helpers/setup_request.ts +++ b/x-pack/plugins/apm/server/lib/helpers/setup_request.ts @@ -42,36 +42,38 @@ export async function setupRequest({ }: APMRouteHandlerResources) { return withApmSpan('setup_request', async () => { const { query } = params; + const coreContext = await context.core; + const licensingContext = await context.licensing; const [indices, includeFrozen] = await Promise.all([ getApmIndices({ - savedObjectsClient: context.core.savedObjects.client, + savedObjectsClient: coreContext.savedObjects.client, config, }), withApmSpan('get_ui_settings', () => - context.core.uiSettings.client.get(UI_SETTINGS.SEARCH_INCLUDE_FROZEN) + coreContext.uiSettings.client.get(UI_SETTINGS.SEARCH_INCLUDE_FROZEN) ), ]); return { indices, apmEventClient: new APMEventClient({ - esClient: context.core.elasticsearch.client.asCurrentUser, + esClient: coreContext.elasticsearch.client.asCurrentUser, debug: query._inspect, request, indices, options: { includeFrozen }, }), - internalClient: createInternalESClient({ + internalClient: await createInternalESClient({ context, request, debug: query._inspect, }), ml: - plugins.ml && isActivePlatinumLicense(context.licensing.license) + plugins.ml && isActivePlatinumLicense(licensingContext.license) ? getMlSetup( plugins.ml.setup, - context.core.savedObjects.client, + coreContext.savedObjects.client, request ) : undefined, @@ -82,7 +84,9 @@ export async function setupRequest({ function getMlSetup( ml: Required['ml']['setup'], - savedObjectsClient: APMRouteHandlerResources['context']['core']['savedObjects']['client'], + savedObjectsClient: Awaited< + APMRouteHandlerResources['context']['core'] + >['savedObjects']['client'], request: KibanaRequest ) { return { diff --git a/x-pack/plugins/apm/server/plugin.ts b/x-pack/plugins/apm/server/plugin.ts index bc4c0d30a9a8..ad058a535206 100644 --- a/x-pack/plugins/apm/server/plugin.ts +++ b/x-pack/plugins/apm/server/plugin.ts @@ -218,12 +218,13 @@ export class APMPlugin request: KibanaRequest; context: ApmPluginRequestHandlerContext; }) => { + const coreContext = await context.core; const [indices, includeFrozen] = await Promise.all([ boundGetApmIndices(), - context.core.uiSettings.client.get(UI_SETTINGS.SEARCH_INCLUDE_FROZEN), + coreContext.uiSettings.client.get(UI_SETTINGS.SEARCH_INCLUDE_FROZEN), ]); - const esClient = context.core.elasticsearch.client.asCurrentUser; + const esClient = coreContext.elasticsearch.client.asCurrentUser; return new APMEventClient({ debug: debug ?? false, diff --git a/x-pack/plugins/apm/server/routes/agent_keys/create_agent_key.ts b/x-pack/plugins/apm/server/routes/agent_keys/create_agent_key.ts index 3b47b81a8641..0bcd85a88ce8 100644 --- a/x-pack/plugins/apm/server/routes/agent_keys/create_agent_key.ts +++ b/x-pack/plugins/apm/server/routes/agent_keys/create_agent_key.ts @@ -26,6 +26,7 @@ export async function createAgentKey({ privileges, resources: [resource], }; + const coreContext = await context.core; // Elasticsearch will allow a user without the right apm privileges to create API keys, but the keys won't validate // check first whether the user has the right privileges, and bail out early if not @@ -33,7 +34,7 @@ export async function createAgentKey({ application: userApplicationPrivileges, username, has_all_requested: hasRequiredPrivileges, - } = await context.core.elasticsearch.client.asCurrentUser.security.hasPrivileges( + } = await coreContext.elasticsearch.client.asCurrentUser.security.hasPrivileges( { body: { application: [application], @@ -80,11 +81,9 @@ export async function createAgentKey({ }; const agentKey = - await context.core.elasticsearch.client.asCurrentUser.security.createApiKey( - { - body, - } - ); + await coreContext.elasticsearch.client.asCurrentUser.security.createApiKey({ + body, + }); return { agentKey, diff --git a/x-pack/plugins/apm/server/routes/agent_keys/get_agent_keys.ts b/x-pack/plugins/apm/server/routes/agent_keys/get_agent_keys.ts index ee01876d743d..239cc6ca896c 100644 --- a/x-pack/plugins/apm/server/routes/agent_keys/get_agent_keys.ts +++ b/x-pack/plugins/apm/server/routes/agent_keys/get_agent_keys.ts @@ -27,7 +27,7 @@ export async function getAgentKeys({ }, }; - const esClient = context.core.elasticsearch.client; + const esClient = (await context.core).elasticsearch.client; const apiResponse = await esClient.asCurrentUser.transport.request<{ api_keys: ApiKey[]; }>({ diff --git a/x-pack/plugins/apm/server/routes/agent_keys/get_agent_keys_privileges.ts b/x-pack/plugins/apm/server/routes/agent_keys/get_agent_keys_privileges.ts index 29514739f222..c9fba5d6ec71 100644 --- a/x-pack/plugins/apm/server/routes/agent_keys/get_agent_keys_privileges.ts +++ b/x-pack/plugins/apm/server/routes/agent_keys/get_agent_keys_privileges.ts @@ -15,8 +15,9 @@ export async function getAgentKeysPrivileges({ context: ApmPluginRequestHandlerContext; securityPluginStart: NonNullable; }) { + const esClient = (await context.core).elasticsearch.client; const [securityHasPrivilegesResponse, areApiKeysEnabled] = await Promise.all([ - context.core.elasticsearch.client.asCurrentUser.security.hasPrivileges({ + esClient.asCurrentUser.security.hasPrivileges({ body: { cluster: ['manage_security', 'manage_api_key', 'manage_own_api_key'], }, diff --git a/x-pack/plugins/apm/server/routes/agent_keys/invalidate_agent_key.ts b/x-pack/plugins/apm/server/routes/agent_keys/invalidate_agent_key.ts index ec244f8e9bee..95ee67f0dff3 100644 --- a/x-pack/plugins/apm/server/routes/agent_keys/invalidate_agent_key.ts +++ b/x-pack/plugins/apm/server/routes/agent_keys/invalidate_agent_key.ts @@ -15,12 +15,11 @@ export async function invalidateAgentKey({ id: string; isAdmin: boolean; }) { + const esClient = (await context.core).elasticsearch.client; const { invalidated_api_keys: invalidatedAgentKeys } = - await context.core.elasticsearch.client.asCurrentUser.security.invalidateApiKey( - { - body: { ids: [id], owner: !isAdmin }, - } - ); + await esClient.asCurrentUser.security.invalidateApiKey({ + body: { ids: [id], owner: !isAdmin }, + }); return { invalidatedAgentKeys, diff --git a/x-pack/plugins/apm/server/routes/correlations/route.ts b/x-pack/plugins/apm/server/routes/correlations/route.ts index 897ca65c37ca..b2bca48574f5 100644 --- a/x-pack/plugins/apm/server/routes/correlations/route.ts +++ b/x-pack/plugins/apm/server/routes/correlations/route.ts @@ -57,12 +57,14 @@ const fieldCandidatesRoute = createApmServerRoute({ options: { tags: ['access:apm'] }, handler: async (resources): Promise<{ fieldCandidates: string[] }> => { const { context } = resources; - if (!isActivePlatinumLicense(context.licensing.license)) { + const { license } = await context.licensing; + if (!isActivePlatinumLicense(license)) { throw Boom.forbidden(INVALID_LICENSE); } const { indices } = await setupRequest(resources); - const esClient = resources.context.core.elasticsearch.client.asCurrentUser; + const esClient = (await resources.context.core).elasticsearch.client + .asCurrentUser; return withApmSpan( 'get_correlations_field_candidates', @@ -102,12 +104,14 @@ const fieldStatsRoute = createApmServerRoute({ errors: any[]; }> => { const { context } = resources; - if (!isActivePlatinumLicense(context.licensing.license)) { + const { license } = await context.licensing; + if (!isActivePlatinumLicense(license)) { throw Boom.forbidden(INVALID_LICENSE); } const { indices } = await setupRequest(resources); - const esClient = resources.context.core.elasticsearch.client.asCurrentUser; + const esClient = (await resources.context.core).elasticsearch.client + .asCurrentUser; const { fieldsToSample, ...params } = resources.params.body; @@ -151,12 +155,14 @@ const fieldValueStatsRoute = createApmServerRoute({ import('./../../../common/correlations/field_stats_types').TopValuesStats > => { const { context } = resources; - if (!isActivePlatinumLicense(context.licensing.license)) { + const { license } = await context.licensing; + if (!isActivePlatinumLicense(license)) { throw Boom.forbidden(INVALID_LICENSE); } const { indices } = await setupRequest(resources); - const esClient = resources.context.core.elasticsearch.client.asCurrentUser; + const esClient = (await resources.context.core).elasticsearch.client + .asCurrentUser; const { fieldName, fieldValue, ...params } = resources.params.query; @@ -202,12 +208,14 @@ const fieldValuePairsRoute = createApmServerRoute({ errors: any[]; }> => { const { context } = resources; - if (!isActivePlatinumLicense(context.licensing.license)) { + const { license } = await context.licensing; + if (!isActivePlatinumLicense(license)) { throw Boom.forbidden(INVALID_LICENSE); } const { indices } = await setupRequest(resources); - const esClient = resources.context.core.elasticsearch.client.asCurrentUser; + const esClient = (await resources.context.core).elasticsearch.client + .asCurrentUser; const { fieldCandidates, ...params } = resources.params.body; @@ -260,12 +268,14 @@ const significantCorrelationsRoute = createApmServerRoute({ fallbackResult?: import('./../../../common/correlations/latency_correlations/types').LatencyCorrelation; }> => { const { context } = resources; - if (!isActivePlatinumLicense(context.licensing.license)) { + const { license } = await context.licensing; + if (!isActivePlatinumLicense(license)) { throw Boom.forbidden(INVALID_LICENSE); } const { indices } = await setupRequest(resources); - const esClient = resources.context.core.elasticsearch.client.asCurrentUser; + const esClient = (await resources.context.core).elasticsearch.client + .asCurrentUser; const { fieldValuePairs, ...params } = resources.params.body; @@ -318,12 +328,14 @@ const pValuesRoute = createApmServerRoute({ fallbackResult?: import('./../../../common/correlations/failed_transactions_correlations/types').FailedTransactionsCorrelation; }> => { const { context } = resources; - if (!isActivePlatinumLicense(context.licensing.license)) { + const { license } = await context.licensing; + if (!isActivePlatinumLicense(license)) { throw Boom.forbidden(INVALID_LICENSE); } const { indices } = await setupRequest(resources); - const esClient = resources.context.core.elasticsearch.client.asCurrentUser; + const esClient = (await resources.context.core).elasticsearch.client + .asCurrentUser; const { fieldCandidates, ...params } = resources.params.body; diff --git a/x-pack/plugins/apm/server/routes/data_view/get_dynamic_data_view.ts b/x-pack/plugins/apm/server/routes/data_view/get_dynamic_data_view.ts index 6a0d52eadd22..8d2c61cce4d8 100644 --- a/x-pack/plugins/apm/server/routes/data_view/get_dynamic_data_view.ts +++ b/x-pack/plugins/apm/server/routes/data_view/get_dynamic_data_view.ts @@ -23,14 +23,15 @@ export const getDynamicDataView = ({ logger, }: Pick) => { return withApmSpan('get_dynamic_data_view', async () => { + const coreContext = await context.core; const apmIndicies = await getApmIndices({ - savedObjectsClient: context.core.savedObjects.client, + savedObjectsClient: coreContext.savedObjects.client, config, }); const dataViewTitle = getApmDataViewTitle(apmIndicies); const DataViewsFetcher = new IndexPatternsFetcher( - context.core.elasticsearch.client.asCurrentUser + coreContext.elasticsearch.client.asCurrentUser ); // Since `getDynamicDataView` is called in setup_request (and thus by every endpoint) diff --git a/x-pack/plugins/apm/server/routes/environments/route.ts b/x-pack/plugins/apm/server/routes/environments/route.ts index f8be45ecf0d5..03fb07fd51cb 100644 --- a/x-pack/plugins/apm/server/routes/environments/route.ts +++ b/x-pack/plugins/apm/server/routes/environments/route.ts @@ -46,7 +46,8 @@ const environmentsRoute = createApmServerRoute({ end, kuery: '', }); - const size = await context.core.uiSettings.client.get( + const coreContext = await context.core; + const size = await coreContext.uiSettings.client.get( maxSuggestions ); const environments = await getEnvironments({ diff --git a/x-pack/plugins/apm/server/routes/fleet/route.ts b/x-pack/plugins/apm/server/routes/fleet/route.ts index 2b6412647641..76f05d50fff4 100644 --- a/x-pack/plugins/apm/server/routes/fleet/route.ts +++ b/x-pack/plugins/apm/server/routes/fleet/route.ts @@ -157,7 +157,7 @@ const getUnsupportedApmServerSchemaRoute = createApmServerRoute({ resources ): Promise<{ unsupported: Array<{ key: string; value: any }> }> => { const { context } = resources; - const savedObjectsClient = context.core.savedObjects.client; + const savedObjectsClient = (await context.core).savedObjects.client; return { unsupported: await getUnsupportedApmServerSchema({ savedObjectsClient }), }; @@ -185,7 +185,7 @@ const getMigrationCheckRoute = createApmServerRoute({ if (!plugins.fleet || !plugins.security) { throw Boom.internal(FLEET_SECURITY_REQUIRED_MESSAGE); } - const savedObjectsClient = context.core.savedObjects.client; + const savedObjectsClient = (await context.core).savedObjects.client; const [fleetPluginStart, securityPluginStart] = await Promise.all([ plugins.fleet.start(), plugins.security.start(), @@ -231,7 +231,7 @@ const createCloudApmPackagePolicyRoute = createApmServerRoute({ if (!plugins.fleet || !plugins.security) { throw Boom.internal(FLEET_SECURITY_REQUIRED_MESSAGE); } - const savedObjectsClient = context.core.savedObjects.client; + const savedObjectsClient = (await context.core).savedObjects.client; const coreStart = await resources.core.start(); const esClient = coreStart.elasticsearch.client.asScoped( resources.request diff --git a/x-pack/plugins/apm/server/routes/service_groups/route.ts b/x-pack/plugins/apm/server/routes/service_groups/route.ts index b9e6a238ba26..160aea5abfe7 100644 --- a/x-pack/plugins/apm/server/routes/service_groups/route.ts +++ b/x-pack/plugins/apm/server/routes/service_groups/route.ts @@ -28,7 +28,7 @@ const serviceGroupsRoute = createApmServerRoute({ resources ): Promise<{ serviceGroups: SavedServiceGroup[] }> => { const { context } = resources; - const savedObjectsClient = context.core.savedObjects.client; + const savedObjectsClient = (await context.core).savedObjects.client; const serviceGroups = await getServiceGroups({ savedObjectsClient }); return { serviceGroups }; }, @@ -46,7 +46,7 @@ const serviceGroupRoute = createApmServerRoute({ }, handler: async (resources): Promise<{ serviceGroup: SavedServiceGroup }> => { const { context, params } = resources; - const savedObjectsClient = context.core.savedObjects.client; + const savedObjectsClient = (await context.core).savedObjects.client; const serviceGroup = await getServiceGroup({ savedObjectsClient, serviceGroupId: params.query.serviceGroup, @@ -75,7 +75,7 @@ const serviceGroupSaveRoute = createApmServerRoute({ handler: async (resources): Promise => { const { context, params } = resources; const { start, end, serviceGroupId } = params.query; - const savedObjectsClient = context.core.savedObjects.client; + const savedObjectsClient = (await context.core).savedObjects.client; const setup = await setupRequest(resources); const items = await lookupServices({ setup, @@ -107,7 +107,7 @@ const serviceGroupDeleteRoute = createApmServerRoute({ handler: async (resources): Promise => { const { context, params } = resources; const { serviceGroupId } = params.query; - const savedObjectsClient = context.core.savedObjects.client; + const savedObjectsClient = (await context.core).savedObjects.client; await deleteServiceGroup({ savedObjectsClient, serviceGroupId, diff --git a/x-pack/plugins/apm/server/routes/service_map/route.ts b/x-pack/plugins/apm/server/routes/service_map/route.ts index b357958d219a..0bad4c251fdd 100644 --- a/x-pack/plugins/apm/server/routes/service_map/route.ts +++ b/x-pack/plugins/apm/server/routes/service_map/route.ts @@ -88,12 +88,14 @@ const serviceMapRoute = createApmServerRoute({ if (!config.serviceMapEnabled) { throw Boom.notFound(); } - if (!isActivePlatinumLicense(context.licensing.license)) { + + const licensingContext = await context.licensing; + if (!isActivePlatinumLicense(licensingContext.license)) { throw Boom.forbidden(invalidLicenseMessage); } notifyFeatureUsage({ - licensingPlugin: context.licensing, + licensingPlugin: licensingContext, featureName: 'serviceMaps', }); @@ -107,7 +109,7 @@ const serviceMapRoute = createApmServerRoute({ }, } = params; - const savedObjectsClient = context.core.savedObjects.client; + const savedObjectsClient = (await context.core).savedObjects.client; const [setup, serviceGroup] = await Promise.all([ setupRequest(resources), serviceGroupId @@ -164,7 +166,9 @@ const serviceMapServiceNodeRoute = createApmServerRoute({ if (!config.serviceMapEnabled) { throw Boom.notFound(); } - if (!isActivePlatinumLicense(context.licensing.license)) { + + const licensingContext = await context.licensing; + if (!isActivePlatinumLicense(licensingContext.license)) { throw Boom.forbidden(invalidLicenseMessage); } const setup = await setupRequest(resources); @@ -226,7 +230,8 @@ const serviceMapBackendNodeRoute = createApmServerRoute({ if (!config.serviceMapEnabled) { throw Boom.notFound(); } - if (!isActivePlatinumLicense(context.licensing.license)) { + const licensingContext = await context.licensing; + if (!isActivePlatinumLicense(licensingContext.license)) { throw Boom.forbidden(invalidLicenseMessage); } const setup = await setupRequest(resources); diff --git a/x-pack/plugins/apm/server/routes/services/route.ts b/x-pack/plugins/apm/server/routes/services/route.ts index 0bcdca6e3cb8..63a52da218d1 100644 --- a/x-pack/plugins/apm/server/routes/services/route.ts +++ b/x-pack/plugins/apm/server/routes/services/route.ts @@ -113,7 +113,7 @@ const servicesRoute = createApmServerRoute({ serviceGroup: serviceGroupId, probability, } = params.query; - const savedObjectsClient = context.core.savedObjects.client; + const savedObjectsClient = (await context.core).savedObjects.client; const [setup, serviceGroup] = await Promise.all([ setupRequest(resources), @@ -403,6 +403,7 @@ const serviceAnnotationsRoute = createApmServerRoute({ const { params, plugins, context, request, logger } = resources; const { serviceName } = params.path; const { environment, start, end } = params.query; + const esClient = (await context.core).elasticsearch.client; const { observability } = plugins; @@ -431,7 +432,7 @@ const serviceAnnotationsRoute = createApmServerRoute({ searchAggregatedTransactions, serviceName, annotationsClient, - client: context.core.elasticsearch.client.asCurrentUser, + client: esClient.asCurrentUser, logger, start, end, @@ -1238,7 +1239,8 @@ const sortedAndFilteredServicesRoute = createApmServerRoute({ }; } - const savedObjectsClient = resources.context.core.savedObjects.client; + const savedObjectsClient = (await resources.context.core).savedObjects + .client; const [setup, serviceGroup] = await Promise.all([ setupRequest(resources), diff --git a/x-pack/plugins/apm/server/routes/settings/agent_configuration/route.ts b/x-pack/plugins/apm/server/routes/settings/agent_configuration/route.ts index 3618647717e5..72869ef165fa 100644 --- a/x-pack/plugins/apm/server/routes/settings/agent_configuration/route.ts +++ b/x-pack/plugins/apm/server/routes/settings/agent_configuration/route.ts @@ -269,6 +269,7 @@ const listAgentConfigurationEnvironmentsRoute = createApmServerRoute({ }> => { const setup = await setupRequest(resources); const { context, params } = resources; + const coreContext = await context.core; const { serviceName, start, end } = params.query; const searchAggregatedTransactions = await getSearchAggregatedTransactions({ @@ -278,7 +279,7 @@ const listAgentConfigurationEnvironmentsRoute = createApmServerRoute({ start, end, }); - const size = await context.core.uiSettings.client.get( + const size = await coreContext.uiSettings.client.get( maxSuggestions ); const environments = await getEnvironments({ diff --git a/x-pack/plugins/apm/server/routes/settings/anomaly_detection/route.ts b/x-pack/plugins/apm/server/routes/settings/anomaly_detection/route.ts index 52e60b019123..fa15ef06ff16 100644 --- a/x-pack/plugins/apm/server/routes/settings/anomaly_detection/route.ts +++ b/x-pack/plugins/apm/server/routes/settings/anomaly_detection/route.ts @@ -37,8 +37,9 @@ const anomalyDetectionJobsRoute = createApmServerRoute({ }> => { const setup = await setupRequest(resources); const { context } = resources; + const licensingContext = await context.licensing; - if (!isActivePlatinumLicense(context.licensing.license)) { + if (!isActivePlatinumLicense(licensingContext.license)) { throw Boom.forbidden(ML_ERRORS.INVALID_LICENSE); } @@ -69,17 +70,18 @@ const createAnomalyDetectionJobsRoute = createApmServerRoute({ handler: async (resources): Promise<{ jobCreated: true }> => { const { params, context, logger } = resources; const { environments } = params.body; + const licensingContext = await context.licensing; const setup = await setupRequest(resources); - if (!isActivePlatinumLicense(context.licensing.license)) { + if (!isActivePlatinumLicense(licensingContext.license)) { throw Boom.forbidden(ML_ERRORS.INVALID_LICENSE); } await createAnomalyDetectionJobs(setup, environments, logger); notifyFeatureUsage({ - licensingPlugin: context.licensing, + licensingPlugin: licensingContext, featureName: 'ml', }); @@ -93,13 +95,14 @@ const anomalyDetectionEnvironmentsRoute = createApmServerRoute({ options: { tags: ['access:apm'] }, handler: async (resources): Promise<{ environments: string[] }> => { const setup = await setupRequest(resources); + const coreContext = await resources.context.core; const searchAggregatedTransactions = await getSearchAggregatedTransactions({ apmEventClient: setup.apmEventClient, config: setup.config, kuery: '', }); - const size = await resources.context.core.uiSettings.client.get( + const size = await coreContext.uiSettings.client.get( maxSuggestions ); const environments = await getAllEnvironments({ diff --git a/x-pack/plugins/apm/server/routes/settings/apm_indices/get_apm_indices.ts b/x-pack/plugins/apm/server/routes/settings/apm_indices/get_apm_indices.ts index 840c15ac1bbc..bebb92d6b644 100644 --- a/x-pack/plugins/apm/server/routes/settings/apm_indices/get_apm_indices.ts +++ b/x-pack/plugins/apm/server/routes/settings/apm_indices/get_apm_indices.ts @@ -74,9 +74,8 @@ export async function getApmIndexSettings({ ReturnType >; try { - apmIndicesSavedObject = await getApmIndicesSavedObject( - context.core.savedObjects.client - ); + const soClient = (await context.core).savedObjects.client; + apmIndicesSavedObject = await getApmIndicesSavedObject(soClient); } catch (error: any) { if (error.output && error.output.statusCode === 404) { apmIndicesSavedObject = {}; diff --git a/x-pack/plugins/apm/server/routes/settings/apm_indices/route.ts b/x-pack/plugins/apm/server/routes/settings/apm_indices/route.ts index cc9f8ef76e4d..796c4312eb2e 100644 --- a/x-pack/plugins/apm/server/routes/settings/apm_indices/route.ts +++ b/x-pack/plugins/apm/server/routes/settings/apm_indices/route.ts @@ -46,8 +46,9 @@ const apmIndicesRoute = createApmServerRoute({ import('./../../../../../observability/common/typings').ApmIndicesConfig > => { const { context, config } = resources; + const savedObjectsClient = (await context.core).savedObjects.client; return await getApmIndices({ - savedObjectsClient: context.core.savedObjects.client, + savedObjectsClient, config, }); }, @@ -80,7 +81,7 @@ const saveApmIndicesRoute = createApmServerRoute({ > => { const { params, context } = resources; const { body } = params; - const savedObjectsClient = context.core.savedObjects.client; + const savedObjectsClient = (await context.core).savedObjects.client; return await saveApmIndices(savedObjectsClient, body); }, }); diff --git a/x-pack/plugins/apm/server/routes/settings/custom_link/route.ts b/x-pack/plugins/apm/server/routes/settings/custom_link/route.ts index ca111a50ce92..1bd6328bdcee 100644 --- a/x-pack/plugins/apm/server/routes/settings/custom_link/route.ts +++ b/x-pack/plugins/apm/server/routes/settings/custom_link/route.ts @@ -54,7 +54,9 @@ const listCustomLinksRoute = createApmServerRoute({ >; }> => { const { context, params } = resources; - if (!isActiveGoldLicense(context.licensing.license)) { + const licensingContext = await context.licensing; + + if (!isActiveGoldLicense(licensingContext.license)) { throw Boom.forbidden(INVALID_LICENSE); } const setup = await setupRequest(resources); @@ -76,14 +78,16 @@ const createCustomLinkRoute = createApmServerRoute({ options: { tags: ['access:apm', 'access:apm_write'] }, handler: async (resources): Promise => { const { context, params } = resources; - if (!isActiveGoldLicense(context.licensing.license)) { + const licensingContext = await context.licensing; + + if (!isActiveGoldLicense(licensingContext.license)) { throw Boom.forbidden(INVALID_LICENSE); } const setup = await setupRequest(resources); const customLink = params.body; notifyFeatureUsage({ - licensingPlugin: context.licensing, + licensingPlugin: licensingContext, featureName: 'customLinks', }); @@ -104,8 +108,9 @@ const updateCustomLinkRoute = createApmServerRoute({ }, handler: async (resources): Promise => { const { params, context } = resources; + const licensingContext = await context.licensing; - if (!isActiveGoldLicense(context.licensing.license)) { + if (!isActiveGoldLicense(licensingContext.license)) { throw Boom.forbidden(INVALID_LICENSE); } const setup = await setupRequest(resources); @@ -133,8 +138,9 @@ const deleteCustomLinkRoute = createApmServerRoute({ }, handler: async (resources): Promise<{ result: string }> => { const { context, params } = resources; + const licensingContext = await context.licensing; - if (!isActiveGoldLicense(context.licensing.license)) { + if (!isActiveGoldLicense(licensingContext.license)) { throw Boom.forbidden(INVALID_LICENSE); } const setup = await setupRequest(resources); diff --git a/x-pack/plugins/apm/server/routes/suggestions/route.ts b/x-pack/plugins/apm/server/routes/suggestions/route.ts index 85e58a815f0d..b49204cd86fc 100644 --- a/x-pack/plugins/apm/server/routes/suggestions/route.ts +++ b/x-pack/plugins/apm/server/routes/suggestions/route.ts @@ -27,7 +27,8 @@ const suggestionsRoute = createApmServerRoute({ config: setup.config, kuery: '', }); - const size = await context.core.uiSettings.client.get( + const coreContext = await context.core; + const size = await coreContext.uiSettings.client.get( maxSuggestions ); const suggestions = await getSuggestions({ diff --git a/x-pack/plugins/apm/server/routes/typings.ts b/x-pack/plugins/apm/server/routes/typings.ts index dac98c9fd4e1..9764b427ee7d 100644 --- a/x-pack/plugins/apm/server/routes/typings.ts +++ b/x-pack/plugins/apm/server/routes/typings.ts @@ -7,7 +7,7 @@ import { CoreSetup, - RequestHandlerContext, + CustomRequestHandlerContext, Logger, KibanaRequest, CoreStart, @@ -24,11 +24,11 @@ import { } from '../types'; import { UxUIFilters } from '../../common/ux_ui_filter'; -export interface ApmPluginRequestHandlerContext extends RequestHandlerContext { +export type ApmPluginRequestHandlerContext = CustomRequestHandlerContext<{ licensing: LicensingApiRequestHandlerContext; alerting: AlertingApiRequestHandlerContext; rac: RacApiRequestHandlerContext; -} +}>; export interface APMRouteCreateOptions { options: { diff --git a/x-pack/plugins/banners/server/routes/info.ts b/x-pack/plugins/banners/server/routes/info.ts index 94574d538c28..17d60cdbc532 100644 --- a/x-pack/plugins/banners/server/routes/info.ts +++ b/x-pack/plugins/banners/server/routes/info.ts @@ -21,11 +21,11 @@ export const registerInfoRoute = (router: BannersRouter, config: BannersConfigTy }, }, async (ctx, req, res) => { - const allowed = isValidLicense(ctx.licensing.license); + const allowed = isValidLicense((await ctx.licensing).license); const bannerConfig = req.auth.isAuthenticated && config.disableSpaceBanners === false - ? await getBannerConfig(ctx.core.uiSettings.client) + ? await getBannerConfig((await ctx.core).uiSettings.client) : config; return res.ok({ diff --git a/x-pack/plugins/banners/server/types.ts b/x-pack/plugins/banners/server/types.ts index e72a5ec99d24..992a182378d6 100644 --- a/x-pack/plugins/banners/server/types.ts +++ b/x-pack/plugins/banners/server/types.ts @@ -5,11 +5,11 @@ * 2.0. */ -import { RequestHandlerContext, IRouter } from '@kbn/core/server'; +import { CustomRequestHandlerContext, IRouter } from '@kbn/core/server'; import { LicensingApiRequestHandlerContext } from '@kbn/licensing-plugin/server'; -export interface BannersRequestHandlerContext extends RequestHandlerContext { +export type BannersRequestHandlerContext = CustomRequestHandlerContext<{ licensing: LicensingApiRequestHandlerContext; -} +}>; export type BannersRouter = IRouter; diff --git a/x-pack/plugins/canvas/server/mocks/workpad_route_context.ts b/x-pack/plugins/canvas/server/mocks/workpad_route_context.ts index 13e4e34b20b6..fb5ad3285e76 100644 --- a/x-pack/plugins/canvas/server/mocks/workpad_route_context.ts +++ b/x-pack/plugins/canvas/server/mocks/workpad_route_context.ts @@ -5,9 +5,10 @@ * 2.0. */ +import { AwaitedProperties } from '@kbn/utility-types'; import { CanvasRouteHandlerContext } from '../workpad_route_context'; -export interface MockWorkpadRouteContext extends CanvasRouteHandlerContext { +export interface MockWorkpadRouteContext extends AwaitedProperties { canvas: { workpad: { create: jest.Mock; diff --git a/x-pack/plugins/canvas/server/routes/custom_elements/create.test.ts b/x-pack/plugins/canvas/server/routes/custom_elements/create.test.ts index e1175191a4c2..69bb51de7d5c 100644 --- a/x-pack/plugins/canvas/server/routes/custom_elements/create.test.ts +++ b/x-pack/plugins/canvas/server/routes/custom_elements/create.test.ts @@ -6,7 +6,8 @@ */ import sinon from 'sinon'; -import { savedObjectsClientMock, httpServerMock } from '@kbn/core/server/mocks'; +import { AwaitedProperties } from '@kbn/utility-types'; +import { savedObjectsClientMock, httpServerMock, coreMock } from '@kbn/core/server/mocks'; import { CUSTOM_ELEMENT_TYPE } from '../../../common/lib/constants'; import { initializeCreateCustomElementRoute } from './create'; import { kibanaResponseFactory, RequestHandlerContext, RequestHandler } from '@kbn/core/server'; @@ -18,7 +19,7 @@ const mockRouteContext = { client: savedObjectsClientMock.create(), }, }, -} as unknown as RequestHandlerContext; +} as unknown as AwaitedProperties; const mockedUUID = '123abc'; const now = new Date(); @@ -54,7 +55,11 @@ describe('POST custom element', () => { body: mockCustomElement, }); - const response = await routeHandler(mockRouteContext, request, kibanaResponseFactory); + const response = await routeHandler( + coreMock.createCustomRequestHandlerContext(mockRouteContext), + request, + kibanaResponseFactory + ); expect(response.status).toBe(200); expect(response.payload).toEqual({ ok: true }); @@ -82,7 +87,11 @@ describe('POST custom element', () => { throw mockRouteContext.core.savedObjects.client.errors.createBadRequestError('bad request'); }); - const response = await routeHandler(mockRouteContext, request, kibanaResponseFactory); + const response = await routeHandler( + coreMock.createCustomRequestHandlerContext(mockRouteContext), + request, + kibanaResponseFactory + ); expect(response.status).toBe(400); }); diff --git a/x-pack/plugins/canvas/server/routes/custom_elements/create.ts b/x-pack/plugins/canvas/server/routes/custom_elements/create.ts index 72780ee35e97..8164c124e27d 100644 --- a/x-pack/plugins/canvas/server/routes/custom_elements/create.ts +++ b/x-pack/plugins/canvas/server/routes/custom_elements/create.ts @@ -34,7 +34,8 @@ export function initializeCreateCustomElementRoute(deps: RouteInitializerDeps) { const now = new Date().toISOString(); const { id, ...payload } = customElement; - await context.core.savedObjects.client.create( + const soClient = (await context.core).savedObjects.client; + await soClient.create( CUSTOM_ELEMENT_TYPE, { ...payload, diff --git a/x-pack/plugins/canvas/server/routes/custom_elements/delete.test.ts b/x-pack/plugins/canvas/server/routes/custom_elements/delete.test.ts index 95d300ca07ed..e989f1c7741f 100644 --- a/x-pack/plugins/canvas/server/routes/custom_elements/delete.test.ts +++ b/x-pack/plugins/canvas/server/routes/custom_elements/delete.test.ts @@ -5,10 +5,11 @@ * 2.0. */ +import { AwaitedProperties } from '@kbn/utility-types'; import { CUSTOM_ELEMENT_TYPE } from '../../../common/lib/constants'; import { initializeDeleteCustomElementRoute } from './delete'; import { kibanaResponseFactory, RequestHandlerContext, RequestHandler } from '@kbn/core/server'; -import { savedObjectsClientMock, httpServerMock } from '@kbn/core/server/mocks'; +import { savedObjectsClientMock, httpServerMock, coreMock } from '@kbn/core/server/mocks'; import { getMockedRouterDeps } from '../test_helpers'; const mockRouteContext = { @@ -17,7 +18,7 @@ const mockRouteContext = { client: savedObjectsClientMock.create(), }, }, -} as unknown as RequestHandlerContext; +} as unknown as AwaitedProperties; describe('DELETE custom element', () => { let routeHandler: RequestHandler; @@ -39,7 +40,11 @@ describe('DELETE custom element', () => { }, }); - const response = await routeHandler(mockRouteContext, request, kibanaResponseFactory); + const response = await routeHandler( + coreMock.createCustomRequestHandlerContext(mockRouteContext), + request, + kibanaResponseFactory + ); expect(response.status).toBe(200); expect(response.payload).toEqual({ ok: true }); @@ -62,7 +67,11 @@ describe('DELETE custom element', () => { throw mockRouteContext.core.savedObjects.client.errors.createBadRequestError('bad request'); }); - const response = await routeHandler(mockRouteContext, request, kibanaResponseFactory); + const response = await routeHandler( + coreMock.createCustomRequestHandlerContext(mockRouteContext), + request, + kibanaResponseFactory + ); expect(response.status).toBe(400); }); diff --git a/x-pack/plugins/canvas/server/routes/custom_elements/delete.ts b/x-pack/plugins/canvas/server/routes/custom_elements/delete.ts index 8e1ae3335c27..60092f2a721f 100644 --- a/x-pack/plugins/canvas/server/routes/custom_elements/delete.ts +++ b/x-pack/plugins/canvas/server/routes/custom_elements/delete.ts @@ -23,7 +23,8 @@ export function initializeDeleteCustomElementRoute(deps: RouteInitializerDeps) { }, }, catchErrorHandler(async (context, request, response) => { - await context.core.savedObjects.client.delete(CUSTOM_ELEMENT_TYPE, request.params.id); + const soClient = (await context.core).savedObjects.client; + await soClient.delete(CUSTOM_ELEMENT_TYPE, request.params.id); return response.ok({ body: okResponse }); }) ); diff --git a/x-pack/plugins/canvas/server/routes/custom_elements/find.test.ts b/x-pack/plugins/canvas/server/routes/custom_elements/find.test.ts index e84d7ec75897..65a9d528a849 100644 --- a/x-pack/plugins/canvas/server/routes/custom_elements/find.test.ts +++ b/x-pack/plugins/canvas/server/routes/custom_elements/find.test.ts @@ -5,9 +5,10 @@ * 2.0. */ +import { AwaitedProperties } from '@kbn/utility-types'; import { initializeFindCustomElementsRoute } from './find'; import { kibanaResponseFactory, RequestHandlerContext, RequestHandler } from '@kbn/core/server'; -import { savedObjectsClientMock, httpServerMock } from '@kbn/core/server/mocks'; +import { savedObjectsClientMock, httpServerMock, coreMock } from '@kbn/core/server/mocks'; import { getMockedRouterDeps } from '../test_helpers'; const mockRouteContext = { @@ -16,7 +17,7 @@ const mockRouteContext = { client: savedObjectsClientMock.create(), }, }, -} as unknown as RequestHandlerContext; +} as unknown as AwaitedProperties; describe('Find custom element', () => { let routeHandler: RequestHandler; @@ -52,7 +53,11 @@ describe('Find custom element', () => { }, }); - const response = await routeHandler(mockRouteContext, request, kibanaResponseFactory); + const response = await routeHandler( + coreMock.createCustomRequestHandlerContext(mockRouteContext), + request, + kibanaResponseFactory + ); expect(response.status).toBe(200); expect(findMock.mock.calls[0][0].search).toBe(`${name}* | ${name}`); expect(findMock.mock.calls[0][0].perPage).toBe(perPage); @@ -88,7 +93,11 @@ describe('Find custom element', () => { }, }); - const response = await routeHandler(mockRouteContext, request, kibanaResponseFactory); + const response = await routeHandler( + coreMock.createCustomRequestHandlerContext(mockRouteContext), + request, + kibanaResponseFactory + ); expect(response.status).toBe(200); expect(response.payload).toMatchInlineSnapshot(` diff --git a/x-pack/plugins/canvas/server/routes/custom_elements/find.ts b/x-pack/plugins/canvas/server/routes/custom_elements/find.ts index edc72208d807..0f7f492cef87 100644 --- a/x-pack/plugins/canvas/server/routes/custom_elements/find.ts +++ b/x-pack/plugins/canvas/server/routes/custom_elements/find.ts @@ -24,7 +24,7 @@ export function initializeFindCustomElementsRoute(deps: RouteInitializerDeps) { }, }, async (context, request, response) => { - const savedObjectsClient = context.core.savedObjects.client; + const savedObjectsClient = (await context.core).savedObjects.client; const { name, page, perPage } = request.query; try { diff --git a/x-pack/plugins/canvas/server/routes/custom_elements/get.test.ts b/x-pack/plugins/canvas/server/routes/custom_elements/get.test.ts index 5f70876bd5f6..e12b1840d58c 100644 --- a/x-pack/plugins/canvas/server/routes/custom_elements/get.test.ts +++ b/x-pack/plugins/canvas/server/routes/custom_elements/get.test.ts @@ -5,10 +5,11 @@ * 2.0. */ +import { AwaitedProperties } from '@kbn/utility-types'; import { CUSTOM_ELEMENT_TYPE } from '../../../common/lib/constants'; import { initializeGetCustomElementRoute } from './get'; import { kibanaResponseFactory, RequestHandlerContext, RequestHandler } from '@kbn/core/server'; -import { savedObjectsClientMock, httpServerMock } from '@kbn/core/server/mocks'; +import { savedObjectsClientMock, httpServerMock, coreMock } from '@kbn/core/server/mocks'; import { getMockedRouterDeps } from '../test_helpers'; const mockRouteContext = { @@ -17,7 +18,7 @@ const mockRouteContext = { client: savedObjectsClientMock.create(), }, }, -} as unknown as RequestHandlerContext; +} as unknown as AwaitedProperties; describe('GET custom element', () => { let routeHandler: RequestHandler; @@ -48,7 +49,11 @@ describe('GET custom element', () => { mockRouteContext.core.savedObjects.client = savedObjectsClient; - const response = await routeHandler(mockRouteContext, request, kibanaResponseFactory); + const response = await routeHandler( + coreMock.createCustomRequestHandlerContext(mockRouteContext), + request, + kibanaResponseFactory + ); expect(response.status).toBe(200); expect(response.payload).toMatchInlineSnapshot(` @@ -84,7 +89,11 @@ describe('GET custom element', () => { }); mockRouteContext.core.savedObjects.client = savedObjectsClient; - const response = await routeHandler(mockRouteContext, request, kibanaResponseFactory); + const response = await routeHandler( + coreMock.createCustomRequestHandlerContext(mockRouteContext), + request, + kibanaResponseFactory + ); expect(response.payload).toMatchInlineSnapshot(` Object { diff --git a/x-pack/plugins/canvas/server/routes/custom_elements/get.ts b/x-pack/plugins/canvas/server/routes/custom_elements/get.ts index 0fd66da28fab..06696ca017ca 100644 --- a/x-pack/plugins/canvas/server/routes/custom_elements/get.ts +++ b/x-pack/plugins/canvas/server/routes/custom_elements/get.ts @@ -23,7 +23,8 @@ export function initializeGetCustomElementRoute(deps: RouteInitializerDeps) { }, }, catchErrorHandler(async (context, request, response) => { - const customElement = await context.core.savedObjects.client.get( + const soClient = (await context.core).savedObjects.client; + const customElement = await soClient.get( CUSTOM_ELEMENT_TYPE, request.params.id ); diff --git a/x-pack/plugins/canvas/server/routes/custom_elements/update.test.ts b/x-pack/plugins/canvas/server/routes/custom_elements/update.test.ts index cb984931d548..cc24f5ad708a 100644 --- a/x-pack/plugins/canvas/server/routes/custom_elements/update.test.ts +++ b/x-pack/plugins/canvas/server/routes/custom_elements/update.test.ts @@ -6,11 +6,12 @@ */ import sinon from 'sinon'; +import { AwaitedProperties } from '@kbn/utility-types'; import { CustomElement } from '../../../types'; import { CUSTOM_ELEMENT_TYPE } from '../../../common/lib/constants'; import { initializeUpdateCustomElementRoute } from './update'; import { kibanaResponseFactory, RequestHandlerContext, RequestHandler } from '@kbn/core/server'; -import { savedObjectsClientMock, httpServerMock } from '@kbn/core/server/mocks'; +import { savedObjectsClientMock, httpServerMock, coreMock } from '@kbn/core/server/mocks'; import { okResponse } from '../ok_response'; import { getMockedRouterDeps } from '../test_helpers'; @@ -20,7 +21,7 @@ const mockRouteContext = { client: savedObjectsClientMock.create(), }, }, -} as unknown as RequestHandlerContext; +} as unknown as AwaitedProperties; const now = new Date(); const nowIso = now.toISOString(); @@ -82,7 +83,11 @@ describe('PUT custom element', () => { mockRouteContext.core.savedObjects.client = savedObjectsClient; - const response = await routeHandler(mockRouteContext, request, kibanaResponseFactory); + const response = await routeHandler( + coreMock.createCustomRequestHandlerContext(mockRouteContext), + request, + kibanaResponseFactory + ); expect(response.status).toBe(200); expect(response.payload).toEqual(okResponse); @@ -117,7 +122,11 @@ describe('PUT custom element', () => { ); }); - const response = await routeHandler(mockRouteContext, request, kibanaResponseFactory); + const response = await routeHandler( + coreMock.createCustomRequestHandlerContext(mockRouteContext), + request, + kibanaResponseFactory + ); expect(response.status).toBe(404); }); @@ -144,7 +153,11 @@ describe('PUT custom element', () => { throw mockRouteContext.core.savedObjects.client.errors.createBadRequestError('bad request'); }); - const response = await routeHandler(mockRouteContext, request, kibanaResponseFactory); + const response = await routeHandler( + coreMock.createCustomRequestHandlerContext(mockRouteContext), + request, + kibanaResponseFactory + ); expect(response.status).toBe(400); }); diff --git a/x-pack/plugins/canvas/server/routes/custom_elements/update.ts b/x-pack/plugins/canvas/server/routes/custom_elements/update.ts index 1604c7b40830..d0c1b8072e1d 100644 --- a/x-pack/plugins/canvas/server/routes/custom_elements/update.ts +++ b/x-pack/plugins/canvas/server/routes/custom_elements/update.ts @@ -37,14 +37,14 @@ export function initializeUpdateCustomElementRoute(deps: RouteInitializerDeps) { const id = request.params.id; const now = new Date().toISOString(); + const soClient = (await context.core).savedObjects.client; - const customElementObject = - await context.core.savedObjects.client.get( - CUSTOM_ELEMENT_TYPE, - id - ); + const customElementObject = await soClient.get( + CUSTOM_ELEMENT_TYPE, + id + ); - await context.core.savedObjects.client.create( + await soClient.create( CUSTOM_ELEMENT_TYPE, { ...customElementObject.attributes, diff --git a/x-pack/plugins/canvas/server/routes/es_fields/es_fields.test.ts b/x-pack/plugins/canvas/server/routes/es_fields/es_fields.test.ts index ec664df330d3..06a30af03c92 100644 --- a/x-pack/plugins/canvas/server/routes/es_fields/es_fields.test.ts +++ b/x-pack/plugins/canvas/server/routes/es_fields/es_fields.test.ts @@ -5,9 +5,10 @@ * 2.0. */ +import { AwaitedProperties } from '@kbn/utility-types'; import { initializeESFieldsRoute } from './es_fields'; import { kibanaResponseFactory, RequestHandlerContext, RequestHandler } from '@kbn/core/server'; -import { httpServerMock, elasticsearchServiceMock } from '@kbn/core/server/mocks'; +import { httpServerMock, elasticsearchServiceMock, coreMock } from '@kbn/core/server/mocks'; import { getMockedRouterDeps } from '../test_helpers'; const mockRouteContext = { @@ -16,7 +17,7 @@ const mockRouteContext = { client: elasticsearchServiceMock.createScopedClusterClient(), }, }, -} as unknown as RequestHandlerContext; +} as unknown as AwaitedProperties; const path = `api/canvas/workpad/find`; @@ -71,7 +72,11 @@ describe('Retrieve ES Fields', () => { fieldCapsMock.mockResolvedValueOnce(mockResults); - const response = await routeHandler(mockRouteContext, request, kibanaResponseFactory); + const response = await routeHandler( + coreMock.createCustomRequestHandlerContext(mockRouteContext), + request, + kibanaResponseFactory + ); expect(response.status).toBe(200); expect(response.payload).toMatchInlineSnapshot(` @@ -99,7 +104,11 @@ describe('Retrieve ES Fields', () => { fieldCapsMock.mockResolvedValueOnce(mockResults); - const response = await routeHandler(mockRouteContext, request, kibanaResponseFactory); + const response = await routeHandler( + coreMock.createCustomRequestHandlerContext(mockRouteContext), + request, + kibanaResponseFactory + ); expect(response.status).toBe(200); expect(response.payload).toMatchInlineSnapshot('Object {}'); @@ -127,7 +136,11 @@ describe('Retrieve ES Fields', () => { fieldCapsMock.mockResolvedValueOnce(mockResults); - const response = await routeHandler(mockRouteContext, request, kibanaResponseFactory); + const response = await routeHandler( + coreMock.createCustomRequestHandlerContext(mockRouteContext), + request, + kibanaResponseFactory + ); expect(response.status).toBe(200); expect(response.payload).toMatchInlineSnapshot(`Object {}`); @@ -148,7 +161,11 @@ describe('Retrieve ES Fields', () => { fieldCapsMock.mockRejectedValueOnce(new Error('Index not found')); await expect( - routeHandler(mockRouteContext, request, kibanaResponseFactory) + routeHandler( + coreMock.createCustomRequestHandlerContext(mockRouteContext), + request, + kibanaResponseFactory + ) ).rejects.toThrowError('Index not found'); }); }); diff --git a/x-pack/plugins/canvas/server/routes/es_fields/es_fields.ts b/x-pack/plugins/canvas/server/routes/es_fields/es_fields.ts index f9523cee0ce4..1923d4648a2a 100644 --- a/x-pack/plugins/canvas/server/routes/es_fields/es_fields.ts +++ b/x-pack/plugins/canvas/server/routes/es_fields/es_fields.ts @@ -28,7 +28,7 @@ export function initializeESFieldsRoute(deps: RouteInitializerDeps) { }, }, catchErrorHandler(async (context, request, response) => { - const client = context.core.elasticsearch.client.asCurrentUser; + const client = (await context.core).elasticsearch.client.asCurrentUser; const { index, fields } = request.query; const config = { diff --git a/x-pack/plugins/canvas/server/routes/templates/list.test.ts b/x-pack/plugins/canvas/server/routes/templates/list.test.ts index eb9703a33a40..a1480eb0eaea 100644 --- a/x-pack/plugins/canvas/server/routes/templates/list.test.ts +++ b/x-pack/plugins/canvas/server/routes/templates/list.test.ts @@ -6,9 +6,10 @@ */ import { badRequest } from '@hapi/boom'; +import { AwaitedProperties } from '@kbn/utility-types'; import { initializeListTemplates } from './list'; import { kibanaResponseFactory, RequestHandlerContext, RequestHandler } from '@kbn/core/server'; -import { savedObjectsClientMock, httpServerMock } from '@kbn/core/server/mocks'; +import { savedObjectsClientMock, httpServerMock, coreMock } from '@kbn/core/server/mocks'; import { getMockedRouterDeps } from '../test_helpers'; const mockRouteContext = { @@ -17,7 +18,7 @@ const mockRouteContext = { client: savedObjectsClientMock.create(), }, }, -} as unknown as RequestHandlerContext; +} as unknown as AwaitedProperties; describe('Find workpad', () => { let routeHandler: RequestHandler; @@ -50,7 +51,11 @@ describe('Find workpad', () => { path: `api/canvas/templates/list`, }); - const response = await routeHandler(mockRouteContext, request, kibanaResponseFactory); + const response = await routeHandler( + coreMock.createCustomRequestHandlerContext(mockRouteContext), + request, + kibanaResponseFactory + ); expect(response.status).toBe(200); expect(response.payload).toMatchInlineSnapshot(` @@ -77,7 +82,11 @@ describe('Find workpad', () => { path: `api/canvas/templates/list`, }); - const response = await routeHandler(mockRouteContext, request, kibanaResponseFactory); + const response = await routeHandler( + coreMock.createCustomRequestHandlerContext(mockRouteContext), + request, + kibanaResponseFactory + ); expect(response.status).toBe(400); expect(response.payload).toMatchInlineSnapshot(` diff --git a/x-pack/plugins/canvas/server/routes/templates/list.ts b/x-pack/plugins/canvas/server/routes/templates/list.ts index 2bff143f127b..c0e32fb3722b 100644 --- a/x-pack/plugins/canvas/server/routes/templates/list.ts +++ b/x-pack/plugins/canvas/server/routes/templates/list.ts @@ -21,7 +21,7 @@ export function initializeListTemplates(deps: RouteInitializerDeps) { }, }, catchErrorHandler(async (context, request, response) => { - const savedObjectsClient = context.core.savedObjects.client; + const savedObjectsClient = (await context.core).savedObjects.client; const templates = await savedObjectsClient.find({ type: TEMPLATE_TYPE, diff --git a/x-pack/plugins/canvas/server/routes/workpad/create.test.ts b/x-pack/plugins/canvas/server/routes/workpad/create.test.ts index 175a1084ec1a..304d7864950e 100644 --- a/x-pack/plugins/canvas/server/routes/workpad/create.test.ts +++ b/x-pack/plugins/canvas/server/routes/workpad/create.test.ts @@ -5,7 +5,8 @@ * 2.0. */ -import { savedObjectsClientMock, httpServerMock } from '@kbn/core/server/mocks'; +import { AwaitedProperties } from '@kbn/utility-types'; +import { savedObjectsClientMock, httpServerMock, coreMock } from '@kbn/core/server/mocks'; import { workpadRouteContextMock, MockWorkpadRouteContext } from '../../mocks'; import { initializeCreateWorkpadRoute } from './create'; import { kibanaResponseFactory, RequestHandler } from '@kbn/core/server'; @@ -18,7 +19,7 @@ let mockRouteContext = { }, }, canvas: workpadRouteContextMock.create(), -} as unknown as MockWorkpadRouteContext; +} as unknown as AwaitedProperties; jest.mock('uuid/v4', () => jest.fn().mockReturnValue('123abc')); @@ -33,7 +34,7 @@ describe('POST workpad', () => { }, }, canvas: workpadRouteContextMock.create(), - } as unknown as MockWorkpadRouteContext; + } as unknown as AwaitedProperties; const routerDeps = getMockedRouterDeps(); initializeCreateWorkpadRoute(routerDeps); @@ -57,7 +58,11 @@ describe('POST workpad', () => { body: mockWorkpad, }); - const response = await routeHandler(mockRouteContext, request, kibanaResponseFactory); + const response = await routeHandler( + coreMock.createCustomRequestHandlerContext(mockRouteContext), + request, + kibanaResponseFactory + ); expect(response.status).toBe(200); expect(response.payload).toEqual({ ok: true, id }); @@ -75,7 +80,11 @@ describe('POST workpad', () => { throw mockRouteContext.core.savedObjects.client.errors.createBadRequestError('bad request'); }); - const response = await routeHandler(mockRouteContext, request, kibanaResponseFactory); + const response = await routeHandler( + coreMock.createCustomRequestHandlerContext(mockRouteContext), + request, + kibanaResponseFactory + ); expect(response.status).toBe(400); }); @@ -109,7 +118,11 @@ describe('POST workpad', () => { body: cloneFromTemplateBody, }); - const response = await routeHandler(mockRouteContext, request, kibanaResponseFactory); + const response = await routeHandler( + coreMock.createCustomRequestHandlerContext(mockRouteContext), + request, + kibanaResponseFactory + ); expect(response.status).toBe(200); expect(response.payload).toEqual({ ok: true, id }); diff --git a/x-pack/plugins/canvas/server/routes/workpad/create.ts b/x-pack/plugins/canvas/server/routes/workpad/create.ts index 8460939c730c..dec03c8945ac 100644 --- a/x-pack/plugins/canvas/server/routes/workpad/create.ts +++ b/x-pack/plugins/canvas/server/routes/workpad/create.ts @@ -50,14 +50,16 @@ export function initializeCreateWorkpadRoute(deps: RouteInitializerDeps) { let workpad = request.body as CanvasWorkpad; if (isCreateFromTemplate(request.body)) { - const templateSavedObject = await context.core.savedObjects.client.get( + const soClient = (await context.core).savedObjects.client; + const templateSavedObject = await soClient.get( TEMPLATE_TYPE, request.body.templateId ); workpad = templateSavedObject.attributes.template; } - const createdObject = await context.canvas.workpad.create(workpad); + const canvasContext = await context.canvas; + const createdObject = await canvasContext.workpad.create(workpad); return response.ok({ body: { ...okResponse, id: createdObject.id }, diff --git a/x-pack/plugins/canvas/server/routes/workpad/delete.test.ts b/x-pack/plugins/canvas/server/routes/workpad/delete.test.ts index fdd390d2954a..f29bad869a8a 100644 --- a/x-pack/plugins/canvas/server/routes/workpad/delete.test.ts +++ b/x-pack/plugins/canvas/server/routes/workpad/delete.test.ts @@ -5,10 +5,11 @@ * 2.0. */ +import { AwaitedProperties } from '@kbn/utility-types'; import { CANVAS_TYPE } from '../../../common/lib/constants'; import { initializeDeleteWorkpadRoute } from './delete'; import { kibanaResponseFactory, RequestHandlerContext, RequestHandler } from '@kbn/core/server'; -import { savedObjectsClientMock, httpServerMock } from '@kbn/core/server/mocks'; +import { savedObjectsClientMock, httpServerMock, coreMock } from '@kbn/core/server/mocks'; import { getMockedRouterDeps } from '../test_helpers'; const mockRouteContext = { @@ -17,7 +18,7 @@ const mockRouteContext = { client: savedObjectsClientMock.create(), }, }, -} as unknown as RequestHandlerContext; +} as unknown as AwaitedProperties; describe('DELETE workpad', () => { let routeHandler: RequestHandler; @@ -39,7 +40,11 @@ describe('DELETE workpad', () => { }, }); - const response = await routeHandler(mockRouteContext, request, kibanaResponseFactory); + const response = await routeHandler( + coreMock.createCustomRequestHandlerContext(mockRouteContext), + request, + kibanaResponseFactory + ); expect(response.status).toBe(200); expect(response.payload).toEqual({ ok: true }); @@ -59,7 +64,11 @@ describe('DELETE workpad', () => { throw mockRouteContext.core.savedObjects.client.errors.createBadRequestError('bad request'); }); - const response = await routeHandler(mockRouteContext, request, kibanaResponseFactory); + const response = await routeHandler( + coreMock.createCustomRequestHandlerContext(mockRouteContext), + request, + kibanaResponseFactory + ); expect(response.status).toBe(400); }); diff --git a/x-pack/plugins/canvas/server/routes/workpad/delete.ts b/x-pack/plugins/canvas/server/routes/workpad/delete.ts index 9075f651abb5..0a1f69e273d0 100644 --- a/x-pack/plugins/canvas/server/routes/workpad/delete.ts +++ b/x-pack/plugins/canvas/server/routes/workpad/delete.ts @@ -23,7 +23,8 @@ export function initializeDeleteWorkpadRoute(deps: RouteInitializerDeps) { }, }, catchErrorHandler(async (context, request, response) => { - context.core.savedObjects.client.delete(CANVAS_TYPE, request.params.id); + const soClient = (await context.core).savedObjects.client; + await soClient.delete(CANVAS_TYPE, request.params.id); return response.ok({ body: okResponse }); }) ); diff --git a/x-pack/plugins/canvas/server/routes/workpad/find.test.ts b/x-pack/plugins/canvas/server/routes/workpad/find.test.ts index 8a54889413d8..39626e95c3f0 100644 --- a/x-pack/plugins/canvas/server/routes/workpad/find.test.ts +++ b/x-pack/plugins/canvas/server/routes/workpad/find.test.ts @@ -5,9 +5,10 @@ * 2.0. */ +import { AwaitedProperties } from '@kbn/utility-types'; import { initializeFindWorkpadsRoute } from './find'; import { kibanaResponseFactory, RequestHandlerContext, RequestHandler } from '@kbn/core/server'; -import { savedObjectsClientMock, httpServerMock } from '@kbn/core/server/mocks'; +import { savedObjectsClientMock, httpServerMock, coreMock } from '@kbn/core/server/mocks'; import { getMockedRouterDeps } from '../test_helpers'; const mockRouteContext = { @@ -16,7 +17,7 @@ const mockRouteContext = { client: savedObjectsClientMock.create(), }, }, -} as unknown as RequestHandlerContext; +} as unknown as AwaitedProperties; describe('Find workpad', () => { let routeHandler: RequestHandler; @@ -52,7 +53,11 @@ describe('Find workpad', () => { }, }); - const response = await routeHandler(mockRouteContext, request, kibanaResponseFactory); + const response = await routeHandler( + coreMock.createCustomRequestHandlerContext(mockRouteContext), + request, + kibanaResponseFactory + ); expect(response.status).toBe(200); expect(findMock.mock.calls[0][0].search).toBe(`${name}* | ${name}`); expect(findMock.mock.calls[0][0].perPage).toBe(perPage); @@ -88,7 +93,11 @@ describe('Find workpad', () => { }, }); - const response = await routeHandler(mockRouteContext, request, kibanaResponseFactory); + const response = await routeHandler( + coreMock.createCustomRequestHandlerContext(mockRouteContext), + request, + kibanaResponseFactory + ); expect(response.status).toBe(200); expect(response.payload).toMatchInlineSnapshot(` diff --git a/x-pack/plugins/canvas/server/routes/workpad/find.ts b/x-pack/plugins/canvas/server/routes/workpad/find.ts index c8953bfab8a7..43db20ee11db 100644 --- a/x-pack/plugins/canvas/server/routes/workpad/find.ts +++ b/x-pack/plugins/canvas/server/routes/workpad/find.ts @@ -24,7 +24,7 @@ export function initializeFindWorkpadsRoute(deps: RouteInitializerDeps) { }, }, async (context, request, response) => { - const savedObjectsClient = context.core.savedObjects.client; + const savedObjectsClient = (await context.core).savedObjects.client; const { name, page, perPage } = request.query; try { diff --git a/x-pack/plugins/canvas/server/routes/workpad/get.test.ts b/x-pack/plugins/canvas/server/routes/workpad/get.test.ts index 735bbfc7209a..217072d04532 100644 --- a/x-pack/plugins/canvas/server/routes/workpad/get.test.ts +++ b/x-pack/plugins/canvas/server/routes/workpad/get.test.ts @@ -5,10 +5,11 @@ * 2.0. */ +import { AwaitedProperties } from '@kbn/utility-types'; import { CANVAS_TYPE } from '../../../common/lib/constants'; import { initializeGetWorkpadRoute } from './get'; import { kibanaResponseFactory, RequestHandler } from '@kbn/core/server'; -import { savedObjectsClientMock, httpServerMock } from '@kbn/core/server/mocks'; +import { savedObjectsClientMock, httpServerMock, coreMock } from '@kbn/core/server/mocks'; import { workpadWithGroupAsElement } from '../../../__fixtures__/workpads'; import { CanvasWorkpad } from '../../../types'; import { getMockedRouterDeps } from '../test_helpers'; @@ -16,7 +17,7 @@ import { workpadRouteContextMock, MockWorkpadRouteContext } from '../../mocks'; const mockRouteContext = { canvas: workpadRouteContextMock.create(), -} as unknown as MockWorkpadRouteContext; +} as unknown as AwaitedProperties; describe('GET workpad', () => { let routeHandler: RequestHandler; @@ -48,7 +49,11 @@ describe('GET workpad', () => { references: [], }); - const response = await routeHandler(mockRouteContext, request, kibanaResponseFactory); + const response = await routeHandler( + coreMock.createCustomRequestHandlerContext(mockRouteContext), + request, + kibanaResponseFactory + ); expect(response.status).toBe(200); expect(response.payload).toMatchInlineSnapshot(` @@ -83,7 +88,11 @@ describe('GET workpad', () => { references: [], }); - const response = await routeHandler(mockRouteContext, request, kibanaResponseFactory); + const response = await routeHandler( + coreMock.createCustomRequestHandlerContext(mockRouteContext), + request, + kibanaResponseFactory + ); const workpad = response.payload as CanvasWorkpad; expect(response.status).toBe(200); @@ -108,7 +117,11 @@ describe('GET workpad', () => { throw savedObjectsClient.errors.createGenericNotFoundError(CANVAS_TYPE, id); }); - const response = await routeHandler(mockRouteContext, request, kibanaResponseFactory); + const response = await routeHandler( + coreMock.createCustomRequestHandlerContext(mockRouteContext), + request, + kibanaResponseFactory + ); expect(response.payload).toMatchInlineSnapshot(` Object { diff --git a/x-pack/plugins/canvas/server/routes/workpad/get.ts b/x-pack/plugins/canvas/server/routes/workpad/get.ts index 8fc31e9850db..fe38b768f64a 100644 --- a/x-pack/plugins/canvas/server/routes/workpad/get.ts +++ b/x-pack/plugins/canvas/server/routes/workpad/get.ts @@ -23,7 +23,8 @@ export function initializeGetWorkpadRoute(deps: RouteInitializerDeps) { }, }, catchErrorHandler(async (context, request, response) => { - const workpad = await context.canvas.workpad.get(request.params.id); + const canvasContext = await context.canvas; + const workpad = await canvasContext.workpad.get(request.params.id); shimWorkpad(workpad); diff --git a/x-pack/plugins/canvas/server/routes/workpad/import.ts b/x-pack/plugins/canvas/server/routes/workpad/import.ts index d8438920aeb5..4734e1e554f6 100644 --- a/x-pack/plugins/canvas/server/routes/workpad/import.ts +++ b/x-pack/plugins/canvas/server/routes/workpad/import.ts @@ -32,7 +32,8 @@ export function initializeImportWorkpadRoute(deps: RouteInitializerDeps) { catchErrorHandler(async (context, request, response) => { const workpad = request.body as ImportedCanvasWorkpad; - const createdObject = await context.canvas.workpad.import(workpad); + const canvasContext = await context.canvas; + const createdObject = await canvasContext.workpad.import(workpad); return response.ok({ body: { ...okResponse, id: createdObject.id }, diff --git a/x-pack/plugins/canvas/server/routes/workpad/resolve.test.ts b/x-pack/plugins/canvas/server/routes/workpad/resolve.test.ts index 94e3f9dd0346..80fde2fadc18 100644 --- a/x-pack/plugins/canvas/server/routes/workpad/resolve.test.ts +++ b/x-pack/plugins/canvas/server/routes/workpad/resolve.test.ts @@ -5,10 +5,11 @@ * 2.0. */ +import { AwaitedProperties } from '@kbn/utility-types'; import { CANVAS_TYPE } from '../../../common/lib/constants'; import { initializeResolveWorkpadRoute } from './resolve'; import { kibanaResponseFactory, RequestHandler } from '@kbn/core/server'; -import { savedObjectsClientMock, httpServerMock } from '@kbn/core/server/mocks'; +import { savedObjectsClientMock, httpServerMock, coreMock } from '@kbn/core/server/mocks'; import { workpadWithGroupAsElement } from '../../../__fixtures__/workpads'; import { CanvasWorkpad } from '../../../types'; import { getMockedRouterDeps } from '../test_helpers'; @@ -16,7 +17,7 @@ import { workpadRouteContextMock, MockWorkpadRouteContext } from '../../mocks'; const mockRouteContext = { canvas: workpadRouteContextMock.create(), -} as unknown as MockWorkpadRouteContext; +} as unknown as AwaitedProperties; describe('RESOLVE workpad', () => { let routeHandler: RequestHandler; @@ -55,7 +56,11 @@ describe('RESOLVE workpad', () => { alias_target_id: aliasId, }); - const response = await routeHandler(mockRouteContext, request, kibanaResponseFactory); + const response = await routeHandler( + coreMock.createCustomRequestHandlerContext(mockRouteContext), + request, + kibanaResponseFactory + ); expect(response.status).toBe(200); expect(response.payload).toMatchInlineSnapshot(` @@ -97,7 +102,11 @@ describe('RESOLVE workpad', () => { outcome: 'exactMatch', }); - const response = await routeHandler(mockRouteContext, request, kibanaResponseFactory); + const response = await routeHandler( + coreMock.createCustomRequestHandlerContext(mockRouteContext), + request, + kibanaResponseFactory + ); const workpad = response.payload.workpad as CanvasWorkpad; expect(response.status).toBe(200); @@ -122,7 +131,11 @@ describe('RESOLVE workpad', () => { throw savedObjectsClient.errors.createGenericNotFoundError(CANVAS_TYPE, id); }); - const response = await routeHandler(mockRouteContext, request, kibanaResponseFactory); + const response = await routeHandler( + coreMock.createCustomRequestHandlerContext(mockRouteContext), + request, + kibanaResponseFactory + ); expect(response.payload).toMatchInlineSnapshot(` Object { diff --git a/x-pack/plugins/canvas/server/routes/workpad/resolve.ts b/x-pack/plugins/canvas/server/routes/workpad/resolve.ts index dbd65e30482f..d2a4b71f35af 100644 --- a/x-pack/plugins/canvas/server/routes/workpad/resolve.ts +++ b/x-pack/plugins/canvas/server/routes/workpad/resolve.ts @@ -23,7 +23,8 @@ export function initializeResolveWorkpadRoute(deps: RouteInitializerDeps) { }, }, catchErrorHandler(async (context, request, response) => { - const resolved = await context.canvas.workpad.resolve(request.params.id); + const canvasContext = await context.canvas; + const resolved = await canvasContext.workpad.resolve(request.params.id); const { saved_object: workpad } = resolved; shimWorkpad(workpad); diff --git a/x-pack/plugins/canvas/server/routes/workpad/update.test.ts b/x-pack/plugins/canvas/server/routes/workpad/update.test.ts index a0aaa05b0f71..2467ed56bdc3 100644 --- a/x-pack/plugins/canvas/server/routes/workpad/update.test.ts +++ b/x-pack/plugins/canvas/server/routes/workpad/update.test.ts @@ -5,11 +5,12 @@ * 2.0. */ +import { AwaitedProperties } from '@kbn/utility-types'; import sinon from 'sinon'; import { CANVAS_TYPE } from '../../../common/lib/constants'; import { initializeUpdateWorkpadRoute, initializeUpdateWorkpadAssetsRoute } from './update'; import { kibanaResponseFactory, RequestHandler } from '@kbn/core/server'; -import { savedObjectsClientMock, httpServerMock } from '@kbn/core/server/mocks'; +import { savedObjectsClientMock, httpServerMock, coreMock } from '@kbn/core/server/mocks'; import { workpads } from '../../../__fixtures__/workpads'; import { okResponse } from '../ok_response'; import { getMockedRouterDeps } from '../test_helpers'; @@ -22,7 +23,7 @@ const mockRouteContext = { }, }, canvas: workpadRouteContextMock.create(), -} as unknown as MockWorkpadRouteContext; +} as unknown as AwaitedProperties; const workpad = workpads[0]; const now = new Date(); @@ -62,7 +63,11 @@ describe('PUT workpad', () => { mockRouteContext.canvas.workpad.update.mockResolvedValue(true); - const response = await routeHandler(mockRouteContext, request, kibanaResponseFactory); + const response = await routeHandler( + coreMock.createCustomRequestHandlerContext(mockRouteContext), + request, + kibanaResponseFactory + ); expect(response.status).toBe(200); expect(response.payload).toEqual(okResponse); @@ -85,7 +90,11 @@ describe('PUT workpad', () => { ); }); - const response = await routeHandler(mockRouteContext, request, kibanaResponseFactory); + const response = await routeHandler( + coreMock.createCustomRequestHandlerContext(mockRouteContext), + request, + kibanaResponseFactory + ); expect(response.status).toBe(404); }); @@ -104,7 +113,11 @@ describe('PUT workpad', () => { throw mockRouteContext.core.savedObjects.client.errors.createBadRequestError('bad request'); }); - const response = await routeHandler(mockRouteContext, request, kibanaResponseFactory); + const response = await routeHandler( + coreMock.createCustomRequestHandlerContext(mockRouteContext), + request, + kibanaResponseFactory + ); expect(response.status).toBe(400); }); @@ -160,7 +173,11 @@ describe('update assets', () => { references: [], }); - const response = await routeHandler(mockRouteContext, request, kibanaResponseFactory); + const response = await routeHandler( + coreMock.createCustomRequestHandlerContext(mockRouteContext), + request, + kibanaResponseFactory + ); expect(response.status).toBe(200); expect(mockRouteContext.canvas.workpad.update).toBeCalledWith(id, { diff --git a/x-pack/plugins/canvas/server/routes/workpad/update.ts b/x-pack/plugins/canvas/server/routes/workpad/update.ts index 9d115c83c1de..d74e57f824f5 100644 --- a/x-pack/plugins/canvas/server/routes/workpad/update.ts +++ b/x-pack/plugins/canvas/server/routes/workpad/update.ts @@ -40,7 +40,8 @@ export function initializeUpdateWorkpadRoute(deps: RouteInitializerDeps) { }, }, catchErrorHandler(async (context, request, response) => { - await context.canvas.workpad.update(request.params.id, request.body as CanvasWorkpad); + const canvasContext = await context.canvas; + await canvasContext.workpad.update(request.params.id, request.body as CanvasWorkpad); return response.ok({ body: okResponse, @@ -65,7 +66,8 @@ export function initializeUpdateWorkpadRoute(deps: RouteInitializerDeps) { }, }, catchErrorHandler(async (context, request, response) => { - await context.canvas.workpad.update(request.params.id, request.body as CanvasWorkpad); + const canvasContext = await context.canvas; + await canvasContext.workpad.update(request.params.id, request.body as CanvasWorkpad); return response.ok({ body: okResponse, @@ -101,7 +103,8 @@ export function initializeUpdateWorkpadAssetsRoute(deps: RouteInitializerDeps) { assets: AssetsRecordSchema.validate(request.body), }; - await context.canvas.workpad.update(request.params.id, workpadAssets as CanvasWorkpad); + const canvasContext = await context.canvas; + await canvasContext.workpad.update(request.params.id, workpadAssets as CanvasWorkpad); return response.ok({ body: okResponse, diff --git a/x-pack/plugins/canvas/server/workpad_route_context.test.ts b/x-pack/plugins/canvas/server/workpad_route_context.test.ts index a1da10b09a91..290eed19d0fc 100644 --- a/x-pack/plugins/canvas/server/workpad_route_context.test.ts +++ b/x-pack/plugins/canvas/server/workpad_route_context.test.ts @@ -7,9 +7,10 @@ import sinon from 'sinon'; import { fromExpression } from '@kbn/interpreter'; +import { AwaitedProperties } from '@kbn/utility-types'; import { createWorkpadRouteContext } from './workpad_route_context'; import { RequestHandlerContext, SavedObjectReference } from '@kbn/core/server'; -import { savedObjectsClientMock } from '@kbn/core/server/mocks'; +import { savedObjectsClientMock, coreMock } from '@kbn/core/server/mocks'; import { CanvasWorkpad } from '../types'; import { CANVAS_TYPE } from '../common/lib/constants'; @@ -26,7 +27,7 @@ const mockContext = { client: savedObjectsClient, }, }, -} as unknown as RequestHandlerContext; +} as unknown as AwaitedProperties; const workpadRouteContext = createWorkpadRouteContext({ expressions: mockedExpressionService as any, @@ -86,7 +87,7 @@ describe('workpad route context', () => { }; const canvasContext = await workpadRouteContext( - mockContext, + coreMock.createCustomRequestHandlerContext(mockContext), undefined as any, undefined as any ); @@ -120,7 +121,7 @@ describe('workpad route context', () => { it('injects references to the saved object', async () => { const id = 'so-id'; const canvasContext = await workpadRouteContext( - mockContext, + coreMock.createCustomRequestHandlerContext(mockContext), undefined as any, undefined as any ); @@ -145,7 +146,7 @@ describe('workpad route context', () => { it('injects references to the saved object', async () => { const id = 'so-id'; const canvasContext = await workpadRouteContext( - mockContext, + coreMock.createCustomRequestHandlerContext(mockContext), undefined as any, undefined as any ); @@ -172,7 +173,7 @@ describe('workpad route context', () => { const createdDate = new Date(2020, 1, 1).toISOString(); const canvasContext = await workpadRouteContext( - mockContext, + coreMock.createCustomRequestHandlerContext(mockContext), undefined as any, undefined as any ); diff --git a/x-pack/plugins/canvas/server/workpad_route_context.ts b/x-pack/plugins/canvas/server/workpad_route_context.ts index c154dd66d76b..0ae1699b8f57 100644 --- a/x-pack/plugins/canvas/server/workpad_route_context.ts +++ b/x-pack/plugins/canvas/server/workpad_route_context.ts @@ -6,7 +6,7 @@ */ import { - RequestHandlerContext, + CustomRequestHandlerContext, RequestHandlerContextProvider, SavedObject, SavedObjectsResolveResponse, @@ -18,7 +18,7 @@ import { injectReferences, extractReferences } from './saved_objects/workpad_ref import { getId } from '../common/lib/get_id'; import { CanvasWorkpad, ImportedCanvasWorkpad } from '../types'; -export interface CanvasRouteHandlerContext extends RequestHandlerContext { +export type CanvasRouteHandlerContext = CustomRequestHandlerContext<{ canvas: { workpad: { create: (attributes: CanvasWorkpad) => Promise>; @@ -31,7 +31,7 @@ export interface CanvasRouteHandlerContext extends RequestHandlerContext { ) => Promise>; }; }; -} +}>; interface Deps { expressions: ExpressionsServiceStart; @@ -40,109 +40,107 @@ interface Deps { export const createWorkpadRouteContext: ( deps: Deps ) => RequestHandlerContextProvider = ({ expressions }) => { - return (context) => ({ - workpad: { - create: async (workpad: CanvasWorkpad) => { - const now = new Date().toISOString(); - const { id: maybeId, ...attributes } = workpad; - - const id = maybeId ? maybeId : getId('workpad'); - - const { workpad: extractedAttributes, references } = extractReferences( - attributes, - expressions - ); - - return await context.core.savedObjects.client.create( - CANVAS_TYPE, - { - ...extractedAttributes, - '@timestamp': now, - '@created': now, - }, - { id, references } - ); - }, - import: async (workpad: ImportedCanvasWorkpad) => { - const now = new Date().toISOString(); - const { id: maybeId, ...workpadWithoutId } = workpad; - - // Functionality of running migrations on import of workpads was implemented in v8.1.0. - // As only attributes of the saved object workpad are exported, to run migrations it is necessary - // to specify the minimal version of possible migrations to execute them. It is v8.0.0 in the current case. - const DEFAULT_MIGRATION_VERSION = { [CANVAS_TYPE]: '8.0.0' }; - const DEFAULT_CORE_MIGRATION_VERSION = '8.0.0'; - - const id = maybeId ? maybeId : getId('workpad'); - - return await context.core.savedObjects.client.create( - CANVAS_TYPE, - { - isWriteable: true, - ...workpadWithoutId, - '@timestamp': now, - '@created': now, - }, - { - migrationVersion: DEFAULT_MIGRATION_VERSION, - coreMigrationVersion: DEFAULT_CORE_MIGRATION_VERSION, + return async (context) => { + const soClient = (await context.core).savedObjects.client; + return { + workpad: { + create: async (workpad: CanvasWorkpad) => { + const now = new Date().toISOString(); + const { id: maybeId, ...attributes } = workpad; + + const id = maybeId ? maybeId : getId('workpad'); + + const { workpad: extractedAttributes, references } = extractReferences( + attributes, + expressions + ); + + return await soClient.create( + CANVAS_TYPE, + { + ...extractedAttributes, + '@timestamp': now, + '@created': now, + }, + { id, references } + ); + }, + import: async (workpad: ImportedCanvasWorkpad) => { + const now = new Date().toISOString(); + const { id: maybeId, ...workpadWithoutId } = workpad; + + // Functionality of running migrations on import of workpads was implemented in v8.1.0. + // As only attributes of the saved object workpad are exported, to run migrations it is necessary + // to specify the minimal version of possible migrations to execute them. It is v8.0.0 in the current case. + const DEFAULT_MIGRATION_VERSION = { [CANVAS_TYPE]: '8.0.0' }; + const DEFAULT_CORE_MIGRATION_VERSION = '8.0.0'; + + const id = maybeId ? maybeId : getId('workpad'); + + return await soClient.create( + CANVAS_TYPE, + { + isWriteable: true, + ...workpadWithoutId, + '@timestamp': now, + '@created': now, + }, + { + migrationVersion: DEFAULT_MIGRATION_VERSION, + coreMigrationVersion: DEFAULT_CORE_MIGRATION_VERSION, + id, + } + ); + }, + get: async (id: string) => { + const workpad = await soClient.get(CANVAS_TYPE, id); + + workpad.attributes = injectReferences( + workpad.attributes, + workpad.references, + expressions + ); + + return workpad; + }, + resolve: async (id: string) => { + const resolved = await soClient.resolve(CANVAS_TYPE, id); + + resolved.saved_object.attributes = injectReferences( + resolved.saved_object.attributes, + resolved.saved_object.references, + expressions + ); + + return resolved; + }, + update: async (id: string, { id: omittedId, ...workpad }: Partial) => { + const now = new Date().toISOString(); + + const workpadObject = await soClient.get(CANVAS_TYPE, id); + + const injectedAttributes = injectReferences( + workpadObject.attributes, + workpadObject.references, + expressions + ); + + const updatedAttributes = { + ...injectedAttributes, + ...workpad, + '@timestamp': now, // always update the modified time + '@created': workpadObject.attributes['@created'], // ensure created is not modified + } as WorkpadAttributes; + + const extracted = extractReferences(updatedAttributes, expressions); + + return await soClient.create(CANVAS_TYPE, extracted.workpad, { + overwrite: true, id, - } - ); - }, - get: async (id: string) => { - const workpad = await context.core.savedObjects.client.get( - CANVAS_TYPE, - id - ); - - workpad.attributes = injectReferences(workpad.attributes, workpad.references, expressions); - - return workpad; - }, - resolve: async (id: string) => { - const resolved = await context.core.savedObjects.client.resolve( - CANVAS_TYPE, - id - ); - - resolved.saved_object.attributes = injectReferences( - resolved.saved_object.attributes, - resolved.saved_object.references, - expressions - ); - - return resolved; - }, - update: async (id: string, { id: omittedId, ...workpad }: Partial) => { - const now = new Date().toISOString(); - - const workpadObject = await context.core.savedObjects.client.get( - CANVAS_TYPE, - id - ); - - const injectedAttributes = injectReferences( - workpadObject.attributes, - workpadObject.references, - expressions - ); - - const updatedAttributes = { - ...injectedAttributes, - ...workpad, - '@timestamp': now, // always update the modified time - '@created': workpadObject.attributes['@created'], // ensure created is not modified - } as WorkpadAttributes; - - const extracted = extractReferences(updatedAttributes, expressions); - - return await context.core.savedObjects.client.create(CANVAS_TYPE, extracted.workpad, { - overwrite: true, - id, - references: extracted.references, - }); + references: extracted.references, + }); + }, }, - }, - }); + }; + }; }; diff --git a/x-pack/plugins/cases/public/components/case_view/case_view_page.test.tsx b/x-pack/plugins/cases/public/components/case_view/case_view_page.test.tsx index a933e823f8b3..639d0617ddb7 100644 --- a/x-pack/plugins/cases/public/components/case_view/case_view_page.test.tsx +++ b/x-pack/plugins/cases/public/components/case_view/case_view_page.test.tsx @@ -36,7 +36,6 @@ jest.mock('../../containers/use_get_case'); jest.mock('../../containers/configure/use_connectors'); jest.mock('../../containers/use_post_push_to_service'); jest.mock('../user_actions/timestamp'); -jest.mock('../../common/lib/kibana'); jest.mock('../../common/navigation/hooks'); const useUpdateCaseMock = useUpdateCase as jest.Mock; @@ -433,7 +432,7 @@ describe('CaseViewPage', () => { }); }); - it('it should call onComponentInitialized on mount', async () => { + it('should call onComponentInitialized on mount', async () => { const onComponentInitialized = jest.fn(); mount( diff --git a/x-pack/plugins/cases/public/components/case_view/case_view_page.tsx b/x-pack/plugins/cases/public/components/case_view/case_view_page.tsx index 72d976f45f61..d95eecde876f 100644 --- a/x-pack/plugins/cases/public/components/case_view/case_view_page.tsx +++ b/x-pack/plugins/cases/public/components/case_view/case_view_page.tsx @@ -6,101 +6,23 @@ */ import React, { useCallback, useEffect, useMemo, useState, useRef } from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiLoadingContent, EuiSpacer } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; -import { CaseStatuses, CaseAttributes, CaseConnector } from '../../../common/api'; -import { Case, UpdateKey, UpdateByKey } from '../../../common/ui'; +import { Case, UpdateKey } from '../../../common/ui'; import { EditableTitle } from '../header_page/editable_title'; -import { TagList } from '../tag_list'; -import { UserActions } from '../user_actions'; -import { UserList } from '../user_list'; -import { useUpdateCase } from '../../containers/use_update_case'; -import { getTypedPayload } from '../../containers/utils'; import { ContentWrapper, WhitePageWrapper } from '../wrappers'; import { CaseActionBar } from '../case_action_bar'; import { useGetCaseUserActions } from '../../containers/use_get_case_user_actions'; -import { EditConnector } from '../edit_connector'; -import { useConnectors } from '../../containers/configure/use_connectors'; -import { normalizeActionConnector, getNoneConnector } from '../configure_cases/utils'; -import { StatusActionButton } from '../status/button'; -import * as i18n from './translations'; import { useTimelineContext } from '../timeline_context/use_timeline_context'; -import { getConnectorById } from '../utils'; import { useCasesContext } from '../cases_context/use_cases_context'; -import { useCaseViewNavigation } from '../../common/navigation'; import { HeaderPage } from '../header_page'; import { useCasesTitleBreadcrumbs } from '../use_breadcrumbs'; import { useGetCaseMetrics } from '../../containers/use_get_case_metrics'; import { CaseViewMetrics } from './metrics'; -import type { CaseViewPageProps, OnUpdateFields } from './types'; +import type { CaseViewPageProps } from './types'; import { useCasesFeatures } from '../cases_context/use_cases_features'; - -const useOnUpdateField = ({ - caseData, - caseId, - handleUpdateField, -}: { - caseData: Case; - caseId: string; - handleUpdateField: (newCase: Case, updateKey: UpdateKey) => void; -}) => { - const { isLoading, updateKey: loadingKey, updateCaseProperty } = useUpdateCase({ caseId }); - - const onUpdateField = useCallback( - ({ key, value, onSuccess, onError }: OnUpdateFields) => { - const callUpdate = (updateKey: UpdateKey, updateValue: UpdateByKey['updateValue']) => - updateCaseProperty({ - updateKey, - updateValue, - updateCase: (newCase) => handleUpdateField(newCase, updateKey), - caseData, - onSuccess, - onError, - }); - - switch (key) { - case 'title': - const titleUpdate = getTypedPayload(value); - if (titleUpdate.length > 0) { - callUpdate('title', titleUpdate); - } - break; - case 'connector': - const connector = getTypedPayload(value); - if (connector != null) { - callUpdate('connector', connector); - } - break; - case 'description': - const descriptionUpdate = getTypedPayload(value); - if (descriptionUpdate.length > 0) { - callUpdate('description', descriptionUpdate); - } - break; - case 'tags': - const tagsUpdate = getTypedPayload(value); - callUpdate('tags', tagsUpdate); - break; - case 'status': - const statusUpdate = getTypedPayload(value); - if (caseData.status !== value) { - callUpdate('status', statusUpdate); - } - break; - case 'settings': - const settingsUpdate = getTypedPayload(value); - if (caseData.settings !== value) { - callUpdate('settings', settingsUpdate); - } - break; - default: - return null; - } - }, - [updateCaseProperty, handleUpdateField, caseData] - ); - return { onUpdateField, isLoading, loadingKey }; -}; +import { useOnUpdateField } from './use_on_update_field'; +import { CaseViewActivity } from './components/case_view_activity'; export const CaseViewPage = React.memo( ({ @@ -108,30 +30,28 @@ export const CaseViewPage = React.memo( caseId, fetchCase, onComponentInitialized, - actionsNavigation, + updateCase, + refreshRef, ruleDetailsNavigation, + actionsNavigation, showAlertDetails, - updateCase, useFetchAlertData, - refreshRef, }) => { const { userCanCrud } = useCasesContext(); const { metricsFeatures } = useCasesFeatures(); - const { getCaseViewUrl } = useCaseViewNavigation(); useCasesTitleBreadcrumbs(caseData.title); const [initLoadingData, setInitLoadingData] = useState(true); const init = useRef(true); const timelineUi = useTimelineContext()?.ui; + const getCaseUserActions = useGetCaseUserActions(caseId, caseData.connector.id); + const { - caseUserActions, fetchCaseUserActions, caseServices, - hasDataToPush, isLoading: isLoadingUserActions, - participants, - } = useGetCaseUserActions(caseId, caseData.connector.id); + } = getCaseUserActions; const refetchCaseUserActions = useCallback(() => { fetchCaseUserActions(caseId, caseData.connector.id); @@ -149,17 +69,8 @@ export const CaseViewPage = React.memo( refetchCaseUserActions(); }, [fetchCase, refetchCaseUserActions, fetchCaseMetrics]); - const handleUpdateCase = useCallback( - (newCase: Case) => { - updateCase(newCase); - fetchCaseUserActions(caseId, newCase.connector.id); - fetchCaseMetrics(); - }, - [updateCase, fetchCaseUserActions, caseId, fetchCaseMetrics] - ); - const handleUpdateField = useCallback( - (newCase: Case, updateKey: UpdateKey) => { + (newCase: Case, _updateKey: UpdateKey) => { updateCase({ ...newCase, comments: caseData.comments }); fetchCaseUserActions(caseId, newCase.connector.id); fetchCaseMetrics(); @@ -202,13 +113,6 @@ export const CaseViewPage = React.memo( updateCase, ]); - const { loading: isLoadingConnectors, connectors } = useConnectors(); - - const [connectorName, isValidConnector] = useMemo(() => { - const connector = connectors.find((c) => c.id === caseData.connector.id); - return [connector?.name ?? '', !!connector]; - }, [connectors, caseData.connector]); - const currentExternalIncident = useMemo( () => caseServices != null && caseServices[caseData.connector.id] != null @@ -217,28 +121,6 @@ export const CaseViewPage = React.memo( [caseServices, caseData.connector] ); - const onSubmitConnector = useCallback( - (connectorId, connectorFields, onError, onSuccess) => { - const connector = getConnectorById(connectorId, connectors); - const connectorToUpdate = connector - ? normalizeActionConnector(connector) - : getNoneConnector(); - - onUpdateField({ - key: 'connector', - value: { ...connectorToUpdate, fields: connectorFields }, - onSuccess, - onError, - }); - }, - [onUpdateField, connectors] - ); - - const onSubmitTags = useCallback( - (newTags) => onUpdateField({ key: 'tags', value: newTags }), - [onUpdateField] - ); - const onSubmitTitle = useCallback( (newTitle) => onUpdateField({ @@ -248,38 +130,12 @@ export const CaseViewPage = React.memo( [onUpdateField] ); - const changeStatus = useCallback( - (status: CaseStatuses) => - onUpdateField({ - key: 'status', - value: status, - }), - [onUpdateField] - ); - - const emailContent = useMemo( - () => ({ - subject: i18n.EMAIL_SUBJECT(caseData.title), - body: i18n.EMAIL_BODY(getCaseViewUrl({ detailName: caseId })), - }), - [caseData.title, getCaseViewUrl, caseId] - ); - useEffect(() => { if (initLoadingData && !isLoadingUserActions) { setInitLoadingData(false); } }, [initLoadingData, isLoadingUserActions]); - const onShowAlertDetails = useCallback( - (alertId: string, index: string) => { - if (showAlertDetails) { - showAlertDetails(alertId, index); - } - }, - [showAlertDetails] - ); - // useEffect used for component's initialization useEffect(() => { if (init.current) { @@ -331,77 +187,18 @@ export const CaseViewPage = React.memo( - - {initLoadingData && ( - - )} - {!initLoadingData && ( - - - - ) : null - } - updateCase={updateCase} - useFetchAlertData={useFetchAlertData} - userCanCrud={userCanCrud} - /> - - - )} - - - - - - - + diff --git a/x-pack/plugins/cases/public/components/case_view/components/case_view_activity.test.tsx b/x-pack/plugins/cases/public/components/case_view/components/case_view_activity.test.tsx new file mode 100644 index 000000000000..463af7cdac8d --- /dev/null +++ b/x-pack/plugins/cases/public/components/case_view/components/case_view_activity.test.tsx @@ -0,0 +1,136 @@ +/* + * 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 { + alertComment, + basicCase, + caseUserActions, + connectorsMock, + getAlertUserAction, +} from '../../../containers/mock'; +import React from 'react'; +import { AppMockRenderer, createAppMockRenderer } from '../../../common/mock'; +import { CaseViewActivity } from './case_view_activity'; +import { ConnectorTypes } from '../../../../common/api/connectors'; +import { Case } from '../../../../common'; +import { CaseViewProps } from '../types'; +import { + UseGetCaseUserActions, + useGetCaseUserActions, +} from '../../../containers/use_get_case_user_actions'; +import { useConnectors } from '../../../containers/configure/use_connectors'; +import { usePostPushToService } from '../../../containers/use_post_push_to_service'; +import { useGetActionLicense } from '../../../containers/use_get_action_license'; +import { useGetTags } from '../../../containers/use_get_tags'; + +jest.mock('../../../containers/use_get_case_user_actions'); +jest.mock('../../../containers/configure/use_connectors'); +jest.mock('../../../containers/use_post_push_to_service'); +jest.mock('../../user_actions/timestamp'); +jest.mock('../../../common/navigation/hooks'); +jest.mock('../../../containers/use_get_action_license'); +jest.mock('../../../containers/use_get_tags'); + +(useGetTags as jest.Mock).mockReturnValue({ tags: ['coke', 'pepsi'], fetchTags: jest.fn() }); +(useGetActionLicense as jest.Mock).mockReturnValue({ + actionLicense: null, + isLoading: false, +}); + +const caseData: Case = { + ...basicCase, + comments: [...basicCase.comments, alertComment], + connector: { + id: 'resilient-2', + name: 'Resilient', + type: ConnectorTypes.resilient, + fields: null, + }, +}; + +const caseViewProps: CaseViewProps = { + onComponentInitialized: jest.fn(), + actionsNavigation: { + href: jest.fn(), + onClick: jest.fn(), + }, + ruleDetailsNavigation: { + href: jest.fn(), + onClick: jest.fn(), + }, + showAlertDetails: jest.fn(), + useFetchAlertData: () => [ + false, + { + 'alert-id-1': '1234', + 'alert-id-2': '1234', + }, + ], +}; +const fetchCaseUserActions = jest.fn(); +const pushCaseToExternalService = jest.fn(); + +const defaultUseGetCaseUserActions = { + caseUserActions: [...caseUserActions, getAlertUserAction()], + caseServices: {}, + fetchCaseUserActions, + firstIndexPushToService: -1, + hasDataToPush: false, + isLoading: false, + isError: false, + lastIndexPushToService: -1, + participants: [caseData.createdBy], +}; + +export const caseProps = { + ...caseViewProps, + initLoadingData: false, + caseId: caseData.id, + caseData, + updateCase: jest.fn(), + fetchCaseMetrics: jest.fn(), + getCaseUserActions: { + caseServices: {}, + caseUserActions: [], + hasDataToPush: false, + isError: false, + isLoading: true, + participants: [], + fetchCaseUserActions: jest.fn(), + } as UseGetCaseUserActions, +}; + +const useGetCaseUserActionsMock = useGetCaseUserActions as jest.Mock; +const useConnectorsMock = useConnectors as jest.Mock; +const usePostPushToServiceMock = usePostPushToService as jest.Mock; + +describe('Case View Page activity tab', () => { + beforeAll(() => { + useGetCaseUserActionsMock.mockReturnValue(defaultUseGetCaseUserActions); + useConnectorsMock.mockReturnValue({ connectors: connectorsMock, loading: false }); + usePostPushToServiceMock.mockReturnValue({ isLoading: false, pushCaseToExternalService }); + }); + let appMockRender: AppMockRenderer; + beforeEach(() => { + appMockRender = createAppMockRenderer(); + }); + it('should render the activity content and main components', () => { + const result = appMockRender.render(); + expect(result.getByTestId('case-view-activity')).toBeTruthy(); + expect(result.getByTestId('user-actions')).toBeTruthy(); + expect(result.getByTestId('case-tags')).toBeTruthy(); + expect(result.getByTestId('connector-edit-header')).toBeTruthy(); + expect(result.getByTestId('case-view-status-action-button')).toBeTruthy(); + }); + + it('should show a loading when initLoadingData is true and hide the user actions activity', () => { + const props = { ...caseProps, initLoadingData: true }; + const result = appMockRender.render(); + expect(result.getByTestId('case-view-loading-content')).toBeTruthy(); + expect(result.queryByTestId('case-view-activity')).toBeFalsy(); + }); +}); diff --git a/x-pack/plugins/cases/public/components/case_view/components/case_view_activity.tsx b/x-pack/plugins/cases/public/components/case_view/components/case_view_activity.tsx new file mode 100644 index 000000000000..b9e4beb5d7e2 --- /dev/null +++ b/x-pack/plugins/cases/public/components/case_view/components/case_view_activity.tsx @@ -0,0 +1,220 @@ +/* + * 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 { EuiFlexGroup, EuiFlexItem, EuiLoadingContent } from '@elastic/eui'; +import React, { useCallback, useMemo } from 'react'; +import { useConnectors } from '../../../containers/configure/use_connectors'; +import { useCaseViewNavigation } from '../../../common/navigation'; +import { UpdateKey, UseFetchAlertData } from '../../../../common/ui/types'; +import { Case, CaseStatuses } from '../../../../common'; +import { EditConnector } from '../../edit_connector'; +import { CasesNavigation } from '../../links'; +import { StatusActionButton } from '../../status/button'; +import { TagList } from '../../tag_list'; +import { UserActions } from '../../user_actions'; +import { UserList } from '../../user_list'; +import { useOnUpdateField } from '../use_on_update_field'; +import { useCasesContext } from '../../cases_context/use_cases_context'; +import * as i18n from '../translations'; +import { getNoneConnector, normalizeActionConnector } from '../../configure_cases/utils'; +import { getConnectorById } from '../../utils'; +import { UseGetCaseUserActions } from '../../../containers/use_get_case_user_actions'; + +export const CaseViewActivity = ({ + initLoadingData, + ruleDetailsNavigation, + caseId, + caseData, + actionsNavigation, + showAlertDetails, + updateCase, + fetchCaseMetrics, + useFetchAlertData, + getCaseUserActions, +}: { + initLoadingData: boolean; + ruleDetailsNavigation?: CasesNavigation; + caseId: string; + caseData: Case; + actionsNavigation?: CasesNavigation; + showAlertDetails?: (alertId: string, index: string) => void; + updateCase: (newCase: Case) => void; + fetchCaseMetrics: (silent?: boolean) => Promise; + useFetchAlertData: UseFetchAlertData; + getCaseUserActions: UseGetCaseUserActions; +}) => { + const { userCanCrud } = useCasesContext(); + const { getCaseViewUrl } = useCaseViewNavigation(); + + const { + caseUserActions, + fetchCaseUserActions, + caseServices, + hasDataToPush, + isLoading: isLoadingUserActions, + participants, + } = getCaseUserActions; + + const refetchCaseUserActions = useCallback(() => { + fetchCaseUserActions(caseId, caseData.connector.id); + }, [caseId, fetchCaseUserActions, caseData]); + + const onShowAlertDetails = useCallback( + (alertId: string, index: string) => { + if (showAlertDetails) { + showAlertDetails(alertId, index); + } + }, + [showAlertDetails] + ); + + const handleUpdateField = useCallback( + (newCase: Case, _updateKey: UpdateKey) => { + updateCase({ ...newCase, comments: caseData.comments }); + fetchCaseUserActions(caseId, newCase.connector.id); + fetchCaseMetrics(); + }, + [updateCase, caseData, fetchCaseUserActions, caseId, fetchCaseMetrics] + ); + + const { onUpdateField, isLoading, loadingKey } = useOnUpdateField({ + caseId, + caseData, + handleUpdateField, + }); + + const changeStatus = useCallback( + (status: CaseStatuses) => + onUpdateField({ + key: 'status', + value: status, + }), + [onUpdateField] + ); + + const emailContent = useMemo( + () => ({ + subject: i18n.EMAIL_SUBJECT(caseData.title), + body: i18n.EMAIL_BODY(getCaseViewUrl({ detailName: caseId })), + }), + [caseData.title, getCaseViewUrl, caseId] + ); + + const onSubmitTags = useCallback( + (newTags) => onUpdateField({ key: 'tags', value: newTags }), + [onUpdateField] + ); + const { loading: isLoadingConnectors, connectors } = useConnectors(); + + const [connectorName, isValidConnector] = useMemo(() => { + const connector = connectors.find((c) => c.id === caseData.connector.id); + return [connector?.name ?? '', !!connector]; + }, [connectors, caseData.connector]); + + const handleUpdateCase = useCallback( + (newCase: Case) => { + updateCase(newCase); + fetchCaseUserActions(caseId, newCase.connector.id); + fetchCaseMetrics(); + }, + [updateCase, fetchCaseUserActions, caseId, fetchCaseMetrics] + ); + + const onSubmitConnector = useCallback( + (connectorId, connectorFields, onError, onSuccess) => { + const connector = getConnectorById(connectorId, connectors); + const connectorToUpdate = connector + ? normalizeActionConnector(connector) + : getNoneConnector(); + + onUpdateField({ + key: 'connector', + value: { ...connectorToUpdate, fields: connectorFields }, + onSuccess, + onError, + }); + }, + [onUpdateField, connectors] + ); + + return ( + <> + + {initLoadingData && ( + + )} + {!initLoadingData && ( + + + + ) : null + } + updateCase={updateCase} + useFetchAlertData={useFetchAlertData} + userCanCrud={userCanCrud} + /> + + + )} + + + + + + + + + ); +}; +CaseViewActivity.displayName = 'CaseViewActivity'; diff --git a/x-pack/plugins/cases/public/components/case_view/use_on_update_field.ts b/x-pack/plugins/cases/public/components/case_view/use_on_update_field.ts new file mode 100644 index 000000000000..dba7719d24fb --- /dev/null +++ b/x-pack/plugins/cases/public/components/case_view/use_on_update_field.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 { useCallback } from 'react'; +import { CaseConnector } from '../../../common/api'; +import { CaseAttributes } from '../../../common/api/cases/case'; +import { CaseStatuses } from '../../../common/api/cases/status'; +import { Case, UpdateByKey, UpdateKey } from '../../containers/types'; +import { useUpdateCase } from '../../containers/use_update_case'; +import { getTypedPayload } from '../../containers/utils'; +import { OnUpdateFields } from './types'; + +export const useOnUpdateField = ({ + caseData, + caseId, + handleUpdateField, +}: { + caseData: Case; + caseId: string; + handleUpdateField: (newCase: Case, updateKey: UpdateKey) => void; +}) => { + const { isLoading, updateKey: loadingKey, updateCaseProperty } = useUpdateCase({ caseId }); + + const onUpdateField = useCallback( + ({ key, value, onSuccess, onError }: OnUpdateFields) => { + const callUpdate = (updateKey: UpdateKey, updateValue: UpdateByKey['updateValue']) => + updateCaseProperty({ + updateKey, + updateValue, + updateCase: (newCase) => handleUpdateField(newCase, updateKey), + caseData, + onSuccess, + onError, + }); + + switch (key) { + case 'title': + const titleUpdate = getTypedPayload(value); + if (titleUpdate.length > 0) { + callUpdate('title', titleUpdate); + } + break; + case 'connector': + const connector = getTypedPayload(value); + if (connector != null) { + callUpdate('connector', connector); + } + break; + case 'description': + const descriptionUpdate = getTypedPayload(value); + if (descriptionUpdate.length > 0) { + callUpdate('description', descriptionUpdate); + } + break; + case 'tags': + const tagsUpdate = getTypedPayload(value); + callUpdate('tags', tagsUpdate); + break; + case 'status': + const statusUpdate = getTypedPayload(value); + if (caseData.status !== value) { + callUpdate('status', statusUpdate); + } + break; + case 'settings': + const settingsUpdate = getTypedPayload(value); + if (caseData.settings !== value) { + callUpdate('settings', settingsUpdate); + } + break; + default: + return null; + } + }, + [updateCaseProperty, handleUpdateField, caseData] + ); + return { onUpdateField, isLoading, loadingKey }; +}; diff --git a/x-pack/plugins/cases/server/plugin.ts b/x-pack/plugins/cases/server/plugin.ts index 52d413cb1ef8..4ec2415bae54 100644 --- a/x-pack/plugins/cases/server/plugin.ts +++ b/x-pack/plugins/cases/server/plugin.ts @@ -6,12 +6,12 @@ */ import { - CoreSetup, - CoreStart, IContextProvider, KibanaRequest, Logger, PluginInitializerContext, + CoreSetup, + CoreStart, } from '@kbn/core/server'; import { SecurityPluginSetup, SecurityPluginStart } from '@kbn/security-plugin/server'; @@ -198,10 +198,11 @@ export class CasePlugin { return { getCasesClient: async () => { const [{ savedObjects }] = await core.getStartServices(); + const coreContext = await context.core; return this.clientFactory.create({ request, - scopedClusterClient: context.core.elasticsearch.client.asCurrentUser, + scopedClusterClient: coreContext.elasticsearch.client.asCurrentUser, savedObjectsService: savedObjects, }); }, diff --git a/x-pack/plugins/cases/server/routes/api/cases/alerts/get_cases.ts b/x-pack/plugins/cases/server/routes/api/cases/alerts/get_cases.ts index 00a368e834a0..8a9c02cf574d 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/alerts/get_cases.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/alerts/get_cases.ts @@ -24,7 +24,8 @@ export const getCasesByAlertIdRoute = createCasesRoute({ try { const alertID = request.params.alert_id; - const casesClient = await context.cases.getCasesClient(); + const caseContext = await context.cases; + const casesClient = await caseContext.getCasesClient(); const options = request.query as CasesByAlertIDRequest; return response.ok({ diff --git a/x-pack/plugins/cases/server/routes/api/cases/delete_cases.ts b/x-pack/plugins/cases/server/routes/api/cases/delete_cases.ts index a63d07037de0..5a397841f976 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/delete_cases.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/delete_cases.ts @@ -21,7 +21,8 @@ export const deleteCaseRoute = createCasesRoute({ }, handler: async ({ context, request, response }) => { try { - const client = await context.cases.getCasesClient(); + const caseContext = await context.cases; + const client = await caseContext.getCasesClient(); await client.cases.delete(request.query.ids); return response.noContent(); diff --git a/x-pack/plugins/cases/server/routes/api/cases/find_cases.ts b/x-pack/plugins/cases/server/routes/api/cases/find_cases.ts index 711c6909df46..2a42bb3fa335 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/find_cases.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/find_cases.ts @@ -15,7 +15,8 @@ export const findCaseRoute = createCasesRoute({ path: `${CASES_URL}/_find`, handler: async ({ context, request, response }) => { try { - const casesClient = await context.cases.getCasesClient(); + const caseContext = await context.cases; + const casesClient = await caseContext.getCasesClient(); const options = request.query as CasesFindRequest; return response.ok({ diff --git a/x-pack/plugins/cases/server/routes/api/cases/get_case.ts b/x-pack/plugins/cases/server/routes/api/cases/get_case.ts index f0e53e82f149..736b9b973ce7 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/get_case.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/get_case.ts @@ -41,7 +41,8 @@ export const getCaseRoute = createCasesRoute({ ); } - const casesClient = await context.cases.getCasesClient(); + const caseContext = await context.cases; + const casesClient = await caseContext.getCasesClient(); const id = request.params.case_id; return response.ok({ @@ -70,7 +71,8 @@ export const resolveCaseRoute = createCasesRoute({ params, handler: async ({ context, request, response }) => { try { - const casesClient = await context.cases.getCasesClient(); + const caseContext = await context.cases; + const casesClient = await caseContext.getCasesClient(); const id = request.params.case_id; return response.ok({ diff --git a/x-pack/plugins/cases/server/routes/api/cases/patch_cases.ts b/x-pack/plugins/cases/server/routes/api/cases/patch_cases.ts index c148a45220a7..bb9649aaa092 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/patch_cases.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/patch_cases.ts @@ -15,7 +15,8 @@ export const patchCaseRoute = createCasesRoute({ path: CASES_URL, handler: async ({ context, request, response }) => { try { - const casesClient = await context.cases.getCasesClient(); + const caseContext = await context.cases; + const casesClient = await caseContext.getCasesClient(); const cases = request.body as CasesPatchRequest; return response.ok({ diff --git a/x-pack/plugins/cases/server/routes/api/cases/post_case.ts b/x-pack/plugins/cases/server/routes/api/cases/post_case.ts index 226d0308a315..8c4d43274f21 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/post_case.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/post_case.ts @@ -15,7 +15,8 @@ export const postCaseRoute = createCasesRoute({ path: CASES_URL, handler: async ({ context, request, response }) => { try { - const casesClient = await context.cases.getCasesClient(); + const caseContext = await context.cases; + const casesClient = await caseContext.getCasesClient(); const theCase = request.body as CasePostRequest; return response.ok({ diff --git a/x-pack/plugins/cases/server/routes/api/cases/push_case.ts b/x-pack/plugins/cases/server/routes/api/cases/push_case.ts index 175838a9d313..9ee30ed34f2a 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/push_case.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/push_case.ts @@ -21,7 +21,8 @@ export const pushCaseRoute: CaseRoute = createCasesRoute({ path: CASE_PUSH_URL, handler: async ({ context, request, response }) => { try { - const casesClient = await context.cases.getCasesClient(); + const caseContext = await context.cases; + const casesClient = await caseContext.getCasesClient(); const params = pipe( CasePushRequestParamsRt.decode(request.params), diff --git a/x-pack/plugins/cases/server/routes/api/cases/reporters/get_reporters.ts b/x-pack/plugins/cases/server/routes/api/cases/reporters/get_reporters.ts index ee413d73565e..56465fd7be1c 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/reporters/get_reporters.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/reporters/get_reporters.ts @@ -15,7 +15,8 @@ export const getReportersRoute = createCasesRoute({ path: CASE_REPORTERS_URL, handler: async ({ context, request, response }) => { try { - const client = await context.cases.getCasesClient(); + const caseContext = await context.cases; + const client = await caseContext.getCasesClient(); const options = request.query as AllReportersFindRequest; return response.ok({ body: await client.cases.getReporters({ ...options }) }); diff --git a/x-pack/plugins/cases/server/routes/api/cases/tags/get_tags.ts b/x-pack/plugins/cases/server/routes/api/cases/tags/get_tags.ts index 7dfa948aa623..8c2071750f5e 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/tags/get_tags.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/tags/get_tags.ts @@ -15,7 +15,8 @@ export const getTagsRoute = createCasesRoute({ path: CASE_TAGS_URL, handler: async ({ context, request, response }) => { try { - const client = await context.cases.getCasesClient(); + const caseContext = await context.cases; + const client = await caseContext.getCasesClient(); const options = request.query as AllTagsFindRequest; return response.ok({ body: await client.cases.getTags({ ...options }) }); diff --git a/x-pack/plugins/cases/server/routes/api/comments/delete_all_comments.ts b/x-pack/plugins/cases/server/routes/api/comments/delete_all_comments.ts index 0a1ebd3b66a7..317ebaec20a1 100644 --- a/x-pack/plugins/cases/server/routes/api/comments/delete_all_comments.ts +++ b/x-pack/plugins/cases/server/routes/api/comments/delete_all_comments.ts @@ -20,7 +20,8 @@ export const deleteAllCommentsRoute = createCasesRoute({ }, handler: async ({ context, request, response }) => { try { - const client = await context.cases.getCasesClient(); + const caseContext = await context.cases; + const client = await caseContext.getCasesClient(); await client.attachments.deleteAll({ caseID: request.params.case_id, diff --git a/x-pack/plugins/cases/server/routes/api/comments/delete_comment.ts b/x-pack/plugins/cases/server/routes/api/comments/delete_comment.ts index 220fbffc76cc..775fabb84640 100644 --- a/x-pack/plugins/cases/server/routes/api/comments/delete_comment.ts +++ b/x-pack/plugins/cases/server/routes/api/comments/delete_comment.ts @@ -22,7 +22,8 @@ export const deleteCommentRoute = createCasesRoute({ }, handler: async ({ context, request, response }) => { try { - const client = await context.cases.getCasesClient(); + const caseContext = await context.cases; + const client = await caseContext.getCasesClient(); await client.attachments.delete({ attachmentID: request.params.comment_id, caseID: request.params.case_id, diff --git a/x-pack/plugins/cases/server/routes/api/comments/find_comments.ts b/x-pack/plugins/cases/server/routes/api/comments/find_comments.ts index 14c6090d62ea..23a7ecb60153 100644 --- a/x-pack/plugins/cases/server/routes/api/comments/find_comments.ts +++ b/x-pack/plugins/cases/server/routes/api/comments/find_comments.ts @@ -32,7 +32,8 @@ export const findCommentsRoute = createCasesRoute({ fold(throwErrors(Boom.badRequest), identity) ); - const client = await context.cases.getCasesClient(); + const caseContext = await context.cases; + const client = await caseContext.getCasesClient(); return response.ok({ body: await client.attachments.find({ caseID: request.params.case_id, diff --git a/x-pack/plugins/cases/server/routes/api/comments/get_alerts.ts b/x-pack/plugins/cases/server/routes/api/comments/get_alerts.ts index 4fa793059ed6..a0b0da466e7c 100644 --- a/x-pack/plugins/cases/server/routes/api/comments/get_alerts.ts +++ b/x-pack/plugins/cases/server/routes/api/comments/get_alerts.ts @@ -23,7 +23,8 @@ export const getAllAlertsAttachedToCaseRoute = createCasesRoute({ try { const caseId = request.params.case_id; - const casesClient = await context.cases.getCasesClient(); + const caseContext = await context.cases; + const casesClient = await caseContext.getCasesClient(); return response.ok({ body: await casesClient.attachments.getAllAlertsAttachToCase({ caseId }), diff --git a/x-pack/plugins/cases/server/routes/api/comments/get_all_comment.ts b/x-pack/plugins/cases/server/routes/api/comments/get_all_comment.ts index 44f8f59550fb..72b8f7e6ac98 100644 --- a/x-pack/plugins/cases/server/routes/api/comments/get_all_comment.ts +++ b/x-pack/plugins/cases/server/routes/api/comments/get_all_comment.ts @@ -23,9 +23,10 @@ export const getAllCommentsRoute = createCasesRoute({ }), }, options: { deprecated: true }, - handler: async ({ context, request, response, logger, kibanaVersion }) => { + handler: async ({ context, request, response }) => { try { - const client = await context.cases.getCasesClient(); + const caseContext = await context.cases; + const client = await caseContext.getCasesClient(); return response.ok({ body: await client.attachments.getAll({ diff --git a/x-pack/plugins/cases/server/routes/api/comments/get_comment.ts b/x-pack/plugins/cases/server/routes/api/comments/get_comment.ts index 91adf832f1ea..2a863f3ee4df 100644 --- a/x-pack/plugins/cases/server/routes/api/comments/get_comment.ts +++ b/x-pack/plugins/cases/server/routes/api/comments/get_comment.ts @@ -22,7 +22,8 @@ export const getCommentRoute = createCasesRoute({ }, handler: async ({ context, request, response }) => { try { - const client = await context.cases.getCasesClient(); + const caseContext = await context.cases; + const client = await caseContext.getCasesClient(); return response.ok({ body: await client.attachments.get({ diff --git a/x-pack/plugins/cases/server/routes/api/comments/patch_comment.ts b/x-pack/plugins/cases/server/routes/api/comments/patch_comment.ts index ebc17daa2561..4bb6493475a9 100644 --- a/x-pack/plugins/cases/server/routes/api/comments/patch_comment.ts +++ b/x-pack/plugins/cases/server/routes/api/comments/patch_comment.ts @@ -31,7 +31,8 @@ export const patchCommentRoute = createCasesRoute({ fold(throwErrors(Boom.badRequest), identity) ); - const client = await context.cases.getCasesClient(); + const caseContext = await context.cases; + const client = await caseContext.getCasesClient(); return response.ok({ body: await client.attachments.update({ diff --git a/x-pack/plugins/cases/server/routes/api/comments/post_comment.ts b/x-pack/plugins/cases/server/routes/api/comments/post_comment.ts index 1ececb365374..f501f3a42580 100644 --- a/x-pack/plugins/cases/server/routes/api/comments/post_comment.ts +++ b/x-pack/plugins/cases/server/routes/api/comments/post_comment.ts @@ -21,7 +21,8 @@ export const postCommentRoute = createCasesRoute({ }, handler: async ({ context, request, response }) => { try { - const casesClient = await context.cases.getCasesClient(); + const caseContext = await context.cases; + const casesClient = await caseContext.getCasesClient(); const caseId = request.params.case_id; const comment = request.body as CommentRequest; diff --git a/x-pack/plugins/cases/server/routes/api/configure/get_configure.ts b/x-pack/plugins/cases/server/routes/api/configure/get_configure.ts index 8dabf7862fc8..72ca1f6a3832 100644 --- a/x-pack/plugins/cases/server/routes/api/configure/get_configure.ts +++ b/x-pack/plugins/cases/server/routes/api/configure/get_configure.ts @@ -15,7 +15,8 @@ export const getCaseConfigureRoute = createCasesRoute({ path: CASE_CONFIGURE_URL, handler: async ({ context, request, response }) => { try { - const client = await context.cases.getCasesClient(); + const caseContext = await context.cases; + const client = await caseContext.getCasesClient(); const options = request.query as GetConfigureFindRequest; return response.ok({ diff --git a/x-pack/plugins/cases/server/routes/api/configure/get_connectors.ts b/x-pack/plugins/cases/server/routes/api/configure/get_connectors.ts index da99cd19065d..4c28b896bd85 100644 --- a/x-pack/plugins/cases/server/routes/api/configure/get_connectors.ts +++ b/x-pack/plugins/cases/server/routes/api/configure/get_connectors.ts @@ -17,7 +17,8 @@ export const getConnectorsRoute = createCasesRoute({ path: `${CASE_CONFIGURE_CONNECTORS_URL}/_find`, handler: async ({ context, response }) => { try { - const client = await context.cases.getCasesClient(); + const caseContext = await context.cases; + const client = await caseContext.getCasesClient(); return response.ok({ body: await client.configure.getConnectors() }); } catch (error) { diff --git a/x-pack/plugins/cases/server/routes/api/configure/patch_configure.ts b/x-pack/plugins/cases/server/routes/api/configure/patch_configure.ts index 40b0d5123f42..b6754dc6da8f 100644 --- a/x-pack/plugins/cases/server/routes/api/configure/patch_configure.ts +++ b/x-pack/plugins/cases/server/routes/api/configure/patch_configure.ts @@ -30,7 +30,8 @@ export const patchCaseConfigureRoute = createCasesRoute({ fold(throwErrors(Boom.badRequest), identity) ); - const client = await context.cases.getCasesClient(); + const caseContext = await context.cases; + const client = await caseContext.getCasesClient(); const configuration = request.body as CasesConfigurePatch; return response.ok({ diff --git a/x-pack/plugins/cases/server/routes/api/configure/post_configure.ts b/x-pack/plugins/cases/server/routes/api/configure/post_configure.ts index bb64175fb52a..102d618fdc05 100644 --- a/x-pack/plugins/cases/server/routes/api/configure/post_configure.ts +++ b/x-pack/plugins/cases/server/routes/api/configure/post_configure.ts @@ -25,7 +25,8 @@ export const postCaseConfigureRoute = createCasesRoute({ fold(throwErrors(Boom.badRequest), identity) ); - const client = await context.cases.getCasesClient(); + const caseContext = await context.cases; + const client = await caseContext.getCasesClient(); return response.ok({ body: await client.configure.create(query), diff --git a/x-pack/plugins/cases/server/routes/api/internal/bulk_create_attachments.ts b/x-pack/plugins/cases/server/routes/api/internal/bulk_create_attachments.ts index 1940cd442eb2..cd2facd2391e 100644 --- a/x-pack/plugins/cases/server/routes/api/internal/bulk_create_attachments.ts +++ b/x-pack/plugins/cases/server/routes/api/internal/bulk_create_attachments.ts @@ -23,7 +23,8 @@ export const bulkCreateAttachmentsRoute = createCasesRoute({ }, handler: async ({ context, request, response }) => { try { - const casesClient = await context.cases.getCasesClient(); + const casesContext = await context.cases; + const casesClient = await casesContext.getCasesClient(); const caseId = request.params.case_id; const attachments = request.body as BulkCreateCommentRequest; diff --git a/x-pack/plugins/cases/server/routes/api/metrics/get_case_metrics.ts b/x-pack/plugins/cases/server/routes/api/metrics/get_case_metrics.ts index b86b84410abe..13bfa2093f62 100644 --- a/x-pack/plugins/cases/server/routes/api/metrics/get_case_metrics.ts +++ b/x-pack/plugins/cases/server/routes/api/metrics/get_case_metrics.ts @@ -24,7 +24,8 @@ export const getCaseMetricRoute = createCasesRoute({ }, handler: async ({ context, request, response }) => { try { - const client = await context.cases.getCasesClient(); + const caseContext = await context.cases; + const client = await caseContext.getCasesClient(); return response.ok({ body: await client.metrics.getCaseMetrics({ caseId: request.params.case_id, diff --git a/x-pack/plugins/cases/server/routes/api/stats/get_status.ts b/x-pack/plugins/cases/server/routes/api/stats/get_status.ts index c245f435835f..d35d366534c1 100644 --- a/x-pack/plugins/cases/server/routes/api/stats/get_status.ts +++ b/x-pack/plugins/cases/server/routes/api/stats/get_status.ts @@ -19,9 +19,10 @@ export const getStatusRoute: CaseRoute = createCasesRoute({ method: 'get', path: CASE_STATUS_URL, options: { deprecated: true }, - handler: async ({ context, request, response, logger, kibanaVersion }) => { + handler: async ({ context, request, response }) => { try { - const client = await context.cases.getCasesClient(); + const caseContext = await context.cases; + const client = await caseContext.getCasesClient(); return response.ok({ body: await client.metrics.getStatusTotalsByType(request.query as CasesStatusRequest), }); diff --git a/x-pack/plugins/cases/server/routes/api/user_actions/get_all_user_actions.ts b/x-pack/plugins/cases/server/routes/api/user_actions/get_all_user_actions.ts index ae686969974e..257db8ab70f0 100644 --- a/x-pack/plugins/cases/server/routes/api/user_actions/get_all_user_actions.ts +++ b/x-pack/plugins/cases/server/routes/api/user_actions/get_all_user_actions.ts @@ -23,9 +23,10 @@ export const getUserActionsRoute = createCasesRoute({ }), }, options: { deprecated: true }, - handler: async ({ context, request, response, logger, kibanaVersion }) => { + handler: async ({ context, request, response }) => { try { - const casesClient = await context.cases.getCasesClient(); + const caseContext = await context.cases; + const casesClient = await caseContext.getCasesClient(); const caseId = request.params.case_id; return response.ok({ diff --git a/x-pack/plugins/cases/server/types.ts b/x-pack/plugins/cases/server/types.ts index 962dc9c85231..2154aec6f532 100644 --- a/x-pack/plugins/cases/server/types.ts +++ b/x-pack/plugins/cases/server/types.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { IRouter, RequestHandlerContext } from '@kbn/core/server'; +import type { IRouter, CustomRequestHandlerContext } from '@kbn/core/server'; import { ActionTypeConfig, ActionTypeSecrets, @@ -22,9 +22,9 @@ export interface CaseRequestContext { /** * @internal */ -export interface CasesRequestHandlerContext extends RequestHandlerContext { +export type CasesRequestHandlerContext = CustomRequestHandlerContext<{ cases: CaseRequestContext; -} +}>; /** * @internal diff --git a/x-pack/plugins/cloud_security_posture/server/create_transforms/create_transforms.ts b/x-pack/plugins/cloud_security_posture/server/create_transforms/create_transforms.ts index 3347d5f36b5d..02217aea3521 100644 --- a/x-pack/plugins/cloud_security_posture/server/create_transforms/create_transforms.ts +++ b/x-pack/plugins/cloud_security_posture/server/create_transforms/create_transforms.ts @@ -11,8 +11,11 @@ import { latestFindingsTransform } from './latest_findings_transform'; import { benchmarkScoreTransform } from './benchmark_score_transform'; // TODO: Move transforms to integration package -export const initializeCspTransforms = async (esClient: ElasticsearchClient, logger: Logger) => { - return Promise.all([ +export const initializeCspTransforms = async ( + esClient: ElasticsearchClient, + logger: Logger +): Promise => { + await Promise.all([ initializeTransform(esClient, latestFindingsTransform, logger), initializeTransform(esClient, benchmarkScoreTransform, logger), ]); diff --git a/x-pack/plugins/cloud_security_posture/server/fleet_integration/fleet_integration.ts b/x-pack/plugins/cloud_security_posture/server/fleet_integration/fleet_integration.ts index 521252f1558e..4693a3e55337 100644 --- a/x-pack/plugins/cloud_security_posture/server/fleet_integration/fleet_integration.ts +++ b/x-pack/plugins/cloud_security_posture/server/fleet_integration/fleet_integration.ts @@ -79,7 +79,7 @@ export const onPackagePolicyDeleteCallback = async ( const { saved_objects: cspRules }: SavedObjectsFindResponse = await soClient.find({ type: cspRuleAssetSavedObjectType, - filter: `csp_rule.attributes.package_policy_id: ${deletedPackagePolicy.id} AND csp_rule.attributes.policy_id: ${deletedPackagePolicy.policy_id}`, + filter: `${cspRuleAssetSavedObjectType}.attributes.package_policy_id: ${deletedPackagePolicy.id} AND ${cspRuleAssetSavedObjectType}.attributes.policy_id: ${deletedPackagePolicy.policy_id}`, }); await Promise.all( cspRules.map((rule) => soClient.delete(cspRuleAssetSavedObjectType, rule.id)) diff --git a/x-pack/plugins/cloud_security_posture/server/plugin.test.ts b/x-pack/plugins/cloud_security_posture/server/plugin.test.ts new file mode 100644 index 000000000000..79ff710fd5c2 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/server/plugin.test.ts @@ -0,0 +1,202 @@ +/* + * 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 { coreMock, httpServerMock, savedObjectsClientMock } from '@kbn/core/server/mocks'; +import { + createPackagePolicyServiceMock, + createArtifactsClientMock, + createMockPackageService, + createMockAgentService, + createMockAgentPolicyService, +} from '@kbn/fleet-plugin/server/mocks'; + +import { createPackagePolicyMock } from '@kbn/fleet-plugin/common/mocks'; +import { dataPluginMock } from '@kbn/data-plugin/server/mocks'; +import { CspPlugin } from './plugin'; +import { CspServerPluginStartDeps } from './types'; +import { createFleetAuthzMock, Installation } from '@kbn/fleet-plugin/common'; +import { + ExternalCallback, + FleetStartContract, + PostPackagePolicyPostCreateCallback, +} from '@kbn/fleet-plugin/server'; +import { CIS_KUBERNETES_PACKAGE_NAME } from '../common/constants'; +import Chance from 'chance'; +import type { AwaitedProperties } from '@kbn/utility-types'; +import type { DeeplyMockedKeys } from '@kbn/utility-types/jest'; +import { RequestHandlerContext } from '@kbn/core/server'; + +const chance = new Chance(); + +const mockRouteContext = { + core: { + savedObjects: { + client: savedObjectsClientMock.create(), + }, + }, +} as unknown as AwaitedProperties; + +const createMockFleetStartContract = (): DeeplyMockedKeys => { + return { + authz: { + fromRequest: jest.fn(async (_) => createFleetAuthzMock()), + }, + fleetSetupCompleted: jest.fn().mockResolvedValue(undefined), + esIndexPatternService: { + getESIndexPattern: jest.fn().mockResolvedValue(undefined), + }, + // @ts-expect-error 2322 + agentService: createMockAgentService(), + // @ts-expect-error 2322 + packageService: createMockPackageService(), + agentPolicyService: createMockAgentPolicyService(), + registerExternalCallback: jest.fn((..._: ExternalCallback) => {}), + packagePolicyService: createPackagePolicyServiceMock(), + createArtifactsClient: jest.fn().mockReturnValue(createArtifactsClientMock()), + }; +}; + +describe('Cloud Security Posture Plugin', () => { + describe('start()', () => { + const fleetMock = createMockFleetStartContract(); + const mockPlugins: CspServerPluginStartDeps = { + fleet: fleetMock, + data: dataPluginMock.createStartContract(), + }; + + const contextMock = coreMock.createCustomRequestHandlerContext(mockRouteContext); + const findMock = mockRouteContext.core.savedObjects.client.find as jest.Mock; + findMock.mockReturnValue( + Promise.resolve({ + saved_objects: [], + total: 0, + per_page: 0, + page: 1, + }) + ); + + let plugin: CspPlugin; + + beforeEach(() => jest.clearAllMocks()); + + it('should initialize when package installed', async () => { + fleetMock.packageService.asInternalUser.getInstallation.mockImplementationOnce( + async (): Promise => { + return {} as jest.Mocked; + } + ); + + const context = coreMock.createPluginInitializerContext(); + plugin = new CspPlugin(context); + const spy = jest.spyOn(plugin, 'initialize').mockImplementation(); + + // Act + await plugin.start(coreMock.createStart(), mockPlugins); + await mockPlugins.fleet.fleetSetupCompleted(); + + // Assert + expect(fleetMock.packageService.asInternalUser.getInstallation).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledTimes(1); + }); + + it('should not initialize when package is not installed', async () => { + fleetMock.packageService.asInternalUser.getInstallation.mockImplementationOnce( + async (): Promise => { + return; + } + ); + + const context = coreMock.createPluginInitializerContext(); + plugin = new CspPlugin(context); + const spy = jest.spyOn(plugin, 'initialize').mockImplementation(); + + // Act + await plugin.start(coreMock.createStart(), mockPlugins); + await mockPlugins.fleet.fleetSetupCompleted(); + + // Assert + expect(fleetMock.packageService.asInternalUser.getInstallation).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledTimes(0); + }); + + it('should initialize when new package is created', async () => { + fleetMock.packageService.asInternalUser.getInstallation.mockImplementationOnce( + async (): Promise => { + return; + } + ); + + const packageMock = createPackagePolicyMock(); + packageMock.package!.name = CIS_KUBERNETES_PACKAGE_NAME; + + const packagePolicyPostCreateCallbacks: PostPackagePolicyPostCreateCallback[] = []; + fleetMock.registerExternalCallback.mockImplementation((...args) => { + if (args[0] === 'packagePolicyPostCreate') { + packagePolicyPostCreateCallbacks.push(args[1]); + } + }); + + const context = coreMock.createPluginInitializerContext(); + plugin = new CspPlugin(context); + const spy = jest.spyOn(plugin, 'initialize').mockImplementation(); + + // Act + await plugin.start(coreMock.createStart(), mockPlugins); + await mockPlugins.fleet.fleetSetupCompleted(); + + // Assert + expect(fleetMock.packageService.asInternalUser.getInstallation).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledTimes(0); + + expect(packagePolicyPostCreateCallbacks.length).toBeGreaterThan(0); + + for (const cb of packagePolicyPostCreateCallbacks) { + await cb(packageMock, contextMock, httpServerMock.createKibanaRequest()); + } + + expect(spy).toHaveBeenCalledTimes(1); + }); + + it('should not initialize when other package is created', async () => { + fleetMock.packageService.asInternalUser.getInstallation.mockImplementationOnce( + async (): Promise => { + return; + } + ); + + const packageMock = createPackagePolicyMock(); + packageMock.package!.name = chance.word(); + + const packagePolicyPostCreateCallbacks: PostPackagePolicyPostCreateCallback[] = []; + fleetMock.registerExternalCallback.mockImplementation((...args) => { + if (args[0] === 'packagePolicyPostCreate') { + packagePolicyPostCreateCallbacks.push(args[1]); + } + }); + + const context = coreMock.createPluginInitializerContext(); + plugin = new CspPlugin(context); + const spy = jest.spyOn(plugin, 'initialize').mockImplementation(); + + // Act + await plugin.start(coreMock.createStart(), mockPlugins); + await mockPlugins.fleet.fleetSetupCompleted(); + + // Assert + expect(fleetMock.packageService.asInternalUser.getInstallation).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledTimes(0); + + expect(packagePolicyPostCreateCallbacks.length).toBeGreaterThan(0); + + for (const cb of packagePolicyPostCreateCallbacks) { + await cb(packageMock, contextMock, httpServerMock.createKibanaRequest()); + } + + expect(spy).toHaveBeenCalledTimes(0); + }); + }); +}); diff --git a/x-pack/plugins/cloud_security_posture/server/plugin.ts b/x-pack/plugins/cloud_security_posture/server/plugin.ts index 99900d4aed0d..5647a9adcd0c 100755 --- a/x-pack/plugins/cloud_security_posture/server/plugin.ts +++ b/x-pack/plugins/cloud_security_posture/server/plugin.ts @@ -6,13 +6,14 @@ */ import type { + KibanaRequest, + RequestHandlerContext, PluginInitializerContext, CoreSetup, CoreStart, Plugin, Logger, } from '@kbn/core/server'; -import { KibanaRequest, RequestHandlerContext } from '@kbn/core/server'; import { DeepReadonly } from 'utility-types'; import { DeletePackagePoliciesResponse, PackagePolicy } from '@kbn/fleet-plugin/common'; import { CspAppService } from './lib/csp_app_services'; @@ -27,6 +28,7 @@ import { defineRoutes } from './routes'; import { cspRuleTemplateAssetType } from './saved_objects/csp_rule_template'; import { cspRuleAssetType } from './saved_objects/csp_rule_type'; import { initializeCspTransformsIndices } from './create_indices/create_transforms_indices'; +import { initializeCspTransforms } from './create_transforms/create_transforms'; import { onPackagePolicyPostCreateCallback, onPackagePolicyDeleteCallback, @@ -48,9 +50,11 @@ export class CspPlugin > { private readonly logger: Logger; + constructor(initializerContext: PluginInitializerContext) { this.logger = initializerContext.logger.get(); } + private readonly CspAppService = new CspAppService(); public setup( @@ -78,21 +82,28 @@ export class CspPlugin ...plugins.fleet, }); - initializeCspTransformsIndices(core.elasticsearch.client.asInternalUser, this.logger); - plugins.fleet.fleetSetupCompleted().then(() => { + plugins.fleet.fleetSetupCompleted().then(async () => { + const packageInfo = await plugins.fleet.packageService.asInternalUser.getInstallation( + CIS_KUBERNETES_PACKAGE_NAME + ); + + // If package is installed we want to make sure all needed assets are installed + if (packageInfo) { + // noinspection ES6MissingAwait + this.initialize(core); + } + plugins.fleet.registerExternalCallback( 'packagePolicyPostCreate', async ( packagePolicy: PackagePolicy, context: RequestHandlerContext, - request: KibanaRequest + _: KibanaRequest ): Promise => { if (packagePolicy.package?.name === CIS_KUBERNETES_PACKAGE_NAME) { - await onPackagePolicyPostCreateCallback( - this.logger, - packagePolicy, - context.core.savedObjects.client - ); + await this.initialize(core); + const soClient = (await context.core).savedObjects.client; + await onPackagePolicyPostCreateCallback(this.logger, packagePolicy, soClient); } return packagePolicy; @@ -117,5 +128,12 @@ export class CspPlugin return {}; } + public stop() {} + + async initialize(core: CoreStart): Promise { + this.logger.debug('initialize'); + await initializeCspTransformsIndices(core.elasticsearch.client.asInternalUser, this.logger); + await initializeCspTransforms(core.elasticsearch.client.asInternalUser, this.logger); + } } diff --git a/x-pack/plugins/cloud_security_posture/server/routes/benchmarks/benchmarks.ts b/x-pack/plugins/cloud_security_posture/server/routes/benchmarks/benchmarks.ts index f0c1dc6e1bf9..2112701f42d4 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/benchmarks/benchmarks.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/benchmarks/benchmarks.ts @@ -146,12 +146,12 @@ export const defineGetBenchmarksRoute = (router: CspRouter, cspContext: CspAppCo validate: { query: benchmarksInputSchema }, }, async (context, request, response) => { - if (!context.fleet.authz.fleet.all) { + if (!(await context.fleet).authz.fleet.all) { return response.forbidden(); } try { - const soClient = context.core.savedObjects.client; + const soClient = (await context.core).savedObjects.client; const { query } = request; const agentService = cspContext.service.agentService; diff --git a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/compliance_dashboard.ts b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/compliance_dashboard.ts index c271208120ae..d1c0d7c0eb01 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/compliance_dashboard.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/compliance_dashboard.ts @@ -44,7 +44,7 @@ export const defineGetComplianceDashboardRoute = ( }, async (context, _, response) => { try { - const esClient = context.core.elasticsearch.client.asCurrentUser; + const esClient = (await context.core).elasticsearch.client.asCurrentUser; const { id: pitId } = await esClient.openPointInTime({ index: LATEST_FINDINGS_INDEX_PATTERN, diff --git a/x-pack/plugins/cloud_security_posture/server/routes/configuration/update_rules_configuration.test.ts b/x-pack/plugins/cloud_security_posture/server/routes/configuration/update_rules_configuration.test.ts index fda7edcf5d87..270466d2e3ad 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/configuration/update_rules_configuration.test.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/configuration/update_rules_configuration.test.ts @@ -110,9 +110,10 @@ describe('Update rules configuration API', () => { }); it('validate getCspRules input parameters', async () => { + const packagePolicy = createPackagePolicyMock(); mockSoClient = savedObjectsClientMock.create(); mockSoClient.find.mockResolvedValueOnce({} as SavedObjectsFindResponse); - await getCspRules(mockSoClient); + await getCspRules(mockSoClient, packagePolicy); expect(mockSoClient.find).toBeCalledTimes(1); expect(mockSoClient.find).toHaveBeenCalledWith( expect.objectContaining({ type: cspRuleAssetSavedObjectType }) @@ -127,23 +128,21 @@ describe('Update rules configuration API', () => { saved_objects: [ { type: 'csp_rule', - id: '1.1.1', - attributes: { enabled: true }, + rego_rule_id: '1.1.1', + attributes: { enabled: true, rego_rule_id: 'cis_1_1_1' }, }, { type: 'csp_rule', - id: '1.1.2', - attributes: { enabled: false }, + attributes: { enabled: false, rego_rule_id: 'cis_1_1_2' }, }, { type: 'csp_rule', - id: '1.1.3', - attributes: { enabled: true }, + attributes: { enabled: true, rego_rule_id: 'cis_1_1_3' }, }, ], - } as SavedObjectsFindResponse; + } as unknown as SavedObjectsFindResponse; const cspConfig = await createRulesConfig(cspRules); - expect(cspConfig).toMatchObject({ activated_rules: { cis_k8s: ['1.1.1', '1.1.3'] } }); + expect(cspConfig).toMatchObject({ activated_rules: { cis_k8s: ['cis_1_1_1', 'cis_1_1_3'] } }); }); it('create empty csp rules config when all rules are disabled', async () => { @@ -154,16 +153,19 @@ describe('Update rules configuration API', () => { saved_objects: [ { type: 'csp_rule', - id: '1.1.1', - attributes: { enabled: false }, + rego_rule_id: '1.1.1', + attributes: { enabled: false, rego_rule_id: 'cis_1_1_1' }, + }, + { + type: 'csp_rule', + attributes: { enabled: false, rego_rule_id: 'cis_1_1_2' }, }, { type: 'csp_rule', - id: '1.1.2', - attributes: { enabled: false }, + attributes: { enabled: false, rego_rule_id: 'cis_1_1_3' }, }, ], - } as SavedObjectsFindResponse; + } as unknown as SavedObjectsFindResponse; const cspConfig = await createRulesConfig(cspRules); expect(cspConfig).toMatchObject({ activated_rules: { cis_k8s: [] } }); }); diff --git a/x-pack/plugins/cloud_security_posture/server/routes/configuration/update_rules_configuration.ts b/x-pack/plugins/cloud_security_posture/server/routes/configuration/update_rules_configuration.ts index 91bc47a19dad..7b9e63f8bd69 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/configuration/update_rules_configuration.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/configuration/update_rules_configuration.ts @@ -44,10 +44,13 @@ export const getPackagePolicy = async ( return packagePolicies![0]; }; -export const getCspRules = async (soClient: SavedObjectsClientContract) => { +export const getCspRules = async ( + soClient: SavedObjectsClientContract, + packagePolicy: PackagePolicy +) => { const cspRules = await soClient.find({ type: cspRuleAssetSavedObjectType, - search: '', + filter: `${cspRuleAssetSavedObjectType}.attributes.package_policy_id: ${packagePolicy.id} AND ${cspRuleAssetSavedObjectType}.attributes.policy_id: ${packagePolicy.policy_id}`, searchFields: ['name'], // TODO: research how to get all rules perPage: 10000, @@ -59,10 +62,9 @@ export const createRulesConfig = ( cspRules: SavedObjectsFindResponse ): CspRulesConfigSchema => { const activatedRules = cspRules.saved_objects.filter((cspRule) => cspRule.attributes.enabled); - const config = { activated_rules: { - cis_k8s: activatedRules.map((activatedRule) => activatedRule.id), + cis_k8s: activatedRules.map((activatedRule) => activatedRule.attributes.rego_rule_id), }, }; return config; @@ -106,13 +108,14 @@ export const defineUpdateRulesConfigRoute = (router: CspRouter, cspContext: CspA validate: { query: configurationUpdateInputSchema }, }, async (context, request, response) => { - if (!context.fleet.authz.fleet.all) { + if (!(await context.fleet).authz.fleet.all) { return response.forbidden(); } try { - const esClient = context.core.elasticsearch.client.asCurrentUser; - const soClient = context.core.savedObjects.client; + const coreContext = await context.core; + const esClient = coreContext.elasticsearch.client.asCurrentUser; + const soClient = coreContext.savedObjects.client; const packagePolicyService = cspContext.service.packagePolicyService; const packagePolicyId = request.query.package_policy_id; @@ -125,7 +128,7 @@ export const defineUpdateRulesConfigRoute = (router: CspRouter, cspContext: CspA packagePolicyId ); - const cspRules = await getCspRules(soClient); + const cspRules = await getCspRules(soClient, packagePolicy); const rulesConfig = createRulesConfig(cspRules); const dataYaml = convertRulesConfigToYaml(rulesConfig); diff --git a/x-pack/plugins/cloud_security_posture/server/routes/findings/findings.ts b/x-pack/plugins/cloud_security_posture/server/routes/findings/findings.ts index 832a9266441f..4c9851be902b 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/findings/findings.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/findings/findings.ts @@ -111,12 +111,12 @@ export const defineFindingsIndexRoute = (router: CspRouter, cspContext: CspAppCo validate: { query: findingsInputSchema }, }, async (context, request, response) => { - if (!context.fleet.authz.fleet.all) { + if (!(await context.fleet).authz.fleet.all) { return response.forbidden(); } try { - const esClient = context.core.elasticsearch.client.asCurrentUser; + const esClient = (await context.core).elasticsearch.client.asCurrentUser; const options = buildOptionsRequest(request.query); const latestCycleIds = diff --git a/x-pack/plugins/cross_cluster_replication/server/routes/api/auto_follow_pattern/register_create_route.ts b/x-pack/plugins/cross_cluster_replication/server/routes/api/auto_follow_pattern/register_create_route.ts index 3fe320fe5bdd..d01ecb03dbae 100644 --- a/x-pack/plugins/cross_cluster_replication/server/routes/api/auto_follow_pattern/register_create_route.ts +++ b/x-pack/plugins/cross_cluster_replication/server/routes/api/auto_follow_pattern/register_create_route.ts @@ -34,7 +34,7 @@ export const registerCreateRoute = ({ }, }, license.guardApiRoute(async (context, request, response) => { - const { client } = context.core.elasticsearch; + const { client } = (await context.core).elasticsearch; const { id, ...rest } = request.body; const body = serializeAutoFollowPattern(rest as AutoFollowPattern); diff --git a/x-pack/plugins/cross_cluster_replication/server/routes/api/auto_follow_pattern/register_delete_route.ts b/x-pack/plugins/cross_cluster_replication/server/routes/api/auto_follow_pattern/register_delete_route.ts index 63ade6d1bd07..95da1239061b 100644 --- a/x-pack/plugins/cross_cluster_replication/server/routes/api/auto_follow_pattern/register_delete_route.ts +++ b/x-pack/plugins/cross_cluster_replication/server/routes/api/auto_follow_pattern/register_delete_route.ts @@ -30,7 +30,7 @@ export const registerDeleteRoute = ({ }, }, license.guardApiRoute(async (context, request, response) => { - const { client } = context.core.elasticsearch; + const { client } = (await context.core).elasticsearch; const { id } = request.params; const ids = id.split(','); diff --git a/x-pack/plugins/cross_cluster_replication/server/routes/api/auto_follow_pattern/register_fetch_route.ts b/x-pack/plugins/cross_cluster_replication/server/routes/api/auto_follow_pattern/register_fetch_route.ts index 7cd9508bd737..006199673410 100644 --- a/x-pack/plugins/cross_cluster_replication/server/routes/api/auto_follow_pattern/register_fetch_route.ts +++ b/x-pack/plugins/cross_cluster_replication/server/routes/api/auto_follow_pattern/register_fetch_route.ts @@ -23,7 +23,7 @@ export const registerFetchRoute = ({ validate: false, }, license.guardApiRoute(async (context, request, response) => { - const { client } = context.core.elasticsearch; + const { client } = (await context.core).elasticsearch; try { const { patterns } = await client.asCurrentUser.ccr.getAutoFollowPattern(); diff --git a/x-pack/plugins/cross_cluster_replication/server/routes/api/auto_follow_pattern/register_get_route.ts b/x-pack/plugins/cross_cluster_replication/server/routes/api/auto_follow_pattern/register_get_route.ts index a5a70805d518..0a0c2f4317d7 100644 --- a/x-pack/plugins/cross_cluster_replication/server/routes/api/auto_follow_pattern/register_get_route.ts +++ b/x-pack/plugins/cross_cluster_replication/server/routes/api/auto_follow_pattern/register_get_route.ts @@ -31,7 +31,7 @@ export const registerGetRoute = ({ }, }, license.guardApiRoute(async (context, request, response) => { - const { client } = context.core.elasticsearch; + const { client } = (await context.core).elasticsearch; const { id } = request.params; try { diff --git a/x-pack/plugins/cross_cluster_replication/server/routes/api/auto_follow_pattern/register_pause_route.ts b/x-pack/plugins/cross_cluster_replication/server/routes/api/auto_follow_pattern/register_pause_route.ts index 1428e33dee66..b355d37fc591 100644 --- a/x-pack/plugins/cross_cluster_replication/server/routes/api/auto_follow_pattern/register_pause_route.ts +++ b/x-pack/plugins/cross_cluster_replication/server/routes/api/auto_follow_pattern/register_pause_route.ts @@ -29,7 +29,7 @@ export const registerPauseRoute = ({ }, }, license.guardApiRoute(async (context, request, response) => { - const { client } = context.core.elasticsearch; + const { client } = (await context.core).elasticsearch; const { id } = request.params; const ids = id.split(','); diff --git a/x-pack/plugins/cross_cluster_replication/server/routes/api/auto_follow_pattern/register_resume_route.ts b/x-pack/plugins/cross_cluster_replication/server/routes/api/auto_follow_pattern/register_resume_route.ts index 912ecd689054..79d31f84398f 100644 --- a/x-pack/plugins/cross_cluster_replication/server/routes/api/auto_follow_pattern/register_resume_route.ts +++ b/x-pack/plugins/cross_cluster_replication/server/routes/api/auto_follow_pattern/register_resume_route.ts @@ -29,7 +29,7 @@ export const registerResumeRoute = ({ }, }, license.guardApiRoute(async (context, request, response) => { - const { client } = context.core.elasticsearch; + const { client } = (await context.core).elasticsearch; const { id } = request.params; const ids = id.split(','); diff --git a/x-pack/plugins/cross_cluster_replication/server/routes/api/auto_follow_pattern/register_update_route.ts b/x-pack/plugins/cross_cluster_replication/server/routes/api/auto_follow_pattern/register_update_route.ts index 98a716eb88cc..0a0ec51ad44d 100644 --- a/x-pack/plugins/cross_cluster_replication/server/routes/api/auto_follow_pattern/register_update_route.ts +++ b/x-pack/plugins/cross_cluster_replication/server/routes/api/auto_follow_pattern/register_update_route.ts @@ -39,7 +39,7 @@ export const registerUpdateRoute = ({ }, }, license.guardApiRoute(async (context, request, response) => { - const { client } = context.core.elasticsearch; + const { client } = (await context.core).elasticsearch; const { id } = request.params; const body = serializeAutoFollowPattern(request.body as AutoFollowPattern); diff --git a/x-pack/plugins/cross_cluster_replication/server/routes/api/cross_cluster_replication/register_permissions_route.ts b/x-pack/plugins/cross_cluster_replication/server/routes/api/cross_cluster_replication/register_permissions_route.ts index dbee279a0133..e4b80b273d4e 100644 --- a/x-pack/plugins/cross_cluster_replication/server/routes/api/cross_cluster_replication/register_permissions_route.ts +++ b/x-pack/plugins/cross_cluster_replication/server/routes/api/cross_cluster_replication/register_permissions_route.ts @@ -22,7 +22,7 @@ export const registerPermissionsRoute = ({ validate: false, }, license.guardApiRoute(async (context, request, response) => { - const { client } = context.core.elasticsearch; + const { client } = (await context.core).elasticsearch; if (!license.isEsSecurityEnabled) { // If security has been disabled in elasticsearch.yml. we'll just let the user use CCR diff --git a/x-pack/plugins/cross_cluster_replication/server/routes/api/cross_cluster_replication/register_stats_route.ts b/x-pack/plugins/cross_cluster_replication/server/routes/api/cross_cluster_replication/register_stats_route.ts index bab31c53a83a..b9bf86d877a6 100644 --- a/x-pack/plugins/cross_cluster_replication/server/routes/api/cross_cluster_replication/register_stats_route.ts +++ b/x-pack/plugins/cross_cluster_replication/server/routes/api/cross_cluster_replication/register_stats_route.ts @@ -23,7 +23,7 @@ export const registerStatsRoute = ({ validate: false, }, license.guardApiRoute(async (context, request, response) => { - const { client } = context.core.elasticsearch; + const { client } = (await context.core).elasticsearch; try { const { auto_follow_stats: autoFollowStats } = await client.asCurrentUser.ccr.stats(); diff --git a/x-pack/plugins/cross_cluster_replication/server/routes/api/follower_index/register_create_route.ts b/x-pack/plugins/cross_cluster_replication/server/routes/api/follower_index/register_create_route.ts index 0aae8f9c115b..84b6096cb3f5 100644 --- a/x-pack/plugins/cross_cluster_replication/server/routes/api/follower_index/register_create_route.ts +++ b/x-pack/plugins/cross_cluster_replication/server/routes/api/follower_index/register_create_route.ts @@ -44,7 +44,7 @@ export const registerCreateRoute = ({ }, }, license.guardApiRoute(async (context, request, response) => { - const { client } = context.core.elasticsearch; + const { client } = (await context.core).elasticsearch; const { name, ...rest } = request.body; const body = removeEmptyFields(serializeFollowerIndex(rest as FollowerIndex)); diff --git a/x-pack/plugins/cross_cluster_replication/server/routes/api/follower_index/register_fetch_route.ts b/x-pack/plugins/cross_cluster_replication/server/routes/api/follower_index/register_fetch_route.ts index 7bbe2c8c05d4..da71d96669ef 100644 --- a/x-pack/plugins/cross_cluster_replication/server/routes/api/follower_index/register_fetch_route.ts +++ b/x-pack/plugins/cross_cluster_replication/server/routes/api/follower_index/register_fetch_route.ts @@ -23,7 +23,7 @@ export const registerFetchRoute = ({ validate: false, }, license.guardApiRoute(async (context, request, response) => { - const { client } = context.core.elasticsearch; + const { client } = (await context.core).elasticsearch; try { const { follower_indices: followerIndices } = await client.asCurrentUser.ccr.followInfo({ diff --git a/x-pack/plugins/cross_cluster_replication/server/routes/api/follower_index/register_get_route.ts b/x-pack/plugins/cross_cluster_replication/server/routes/api/follower_index/register_get_route.ts index c33ed97c289f..44babd1bbcc4 100644 --- a/x-pack/plugins/cross_cluster_replication/server/routes/api/follower_index/register_get_route.ts +++ b/x-pack/plugins/cross_cluster_replication/server/routes/api/follower_index/register_get_route.ts @@ -30,7 +30,7 @@ export const registerGetRoute = ({ }, }, license.guardApiRoute(async (context, request, response) => { - const { client } = context.core.elasticsearch; + const { client } = (await context.core).elasticsearch; const { id } = request.params; try { diff --git a/x-pack/plugins/cross_cluster_replication/server/routes/api/follower_index/register_pause_route.ts b/x-pack/plugins/cross_cluster_replication/server/routes/api/follower_index/register_pause_route.ts index 6c888e0a6e8b..3d77ffefec9f 100644 --- a/x-pack/plugins/cross_cluster_replication/server/routes/api/follower_index/register_pause_route.ts +++ b/x-pack/plugins/cross_cluster_replication/server/routes/api/follower_index/register_pause_route.ts @@ -27,7 +27,7 @@ export const registerPauseRoute = ({ }, }, license.guardApiRoute(async (context, request, response) => { - const { client } = context.core.elasticsearch; + const { client } = (await context.core).elasticsearch; const { id } = request.params; const ids = id.split(','); diff --git a/x-pack/plugins/cross_cluster_replication/server/routes/api/follower_index/register_resume_route.ts b/x-pack/plugins/cross_cluster_replication/server/routes/api/follower_index/register_resume_route.ts index 206de7d62fb3..da01b3d0e891 100644 --- a/x-pack/plugins/cross_cluster_replication/server/routes/api/follower_index/register_resume_route.ts +++ b/x-pack/plugins/cross_cluster_replication/server/routes/api/follower_index/register_resume_route.ts @@ -27,7 +27,7 @@ export const registerResumeRoute = ({ }, }, license.guardApiRoute(async (context, request, response) => { - const { client } = context.core.elasticsearch; + const { client } = (await context.core).elasticsearch; const { id } = request.params; const ids = id.split(','); diff --git a/x-pack/plugins/cross_cluster_replication/server/routes/api/follower_index/register_unfollow_route.ts b/x-pack/plugins/cross_cluster_replication/server/routes/api/follower_index/register_unfollow_route.ts index e240a10df068..f7987029ebc7 100644 --- a/x-pack/plugins/cross_cluster_replication/server/routes/api/follower_index/register_unfollow_route.ts +++ b/x-pack/plugins/cross_cluster_replication/server/routes/api/follower_index/register_unfollow_route.ts @@ -27,7 +27,7 @@ export const registerUnfollowRoute = ({ }, }, license.guardApiRoute(async (context, request, response) => { - const { client } = context.core.elasticsearch; + const { client } = (await context.core).elasticsearch; const { id } = request.params; const ids = id.split(','); diff --git a/x-pack/plugins/cross_cluster_replication/server/routes/api/follower_index/register_update_route.ts b/x-pack/plugins/cross_cluster_replication/server/routes/api/follower_index/register_update_route.ts index 0bacbd5fb345..3c0850ac5984 100644 --- a/x-pack/plugins/cross_cluster_replication/server/routes/api/follower_index/register_update_route.ts +++ b/x-pack/plugins/cross_cluster_replication/server/routes/api/follower_index/register_update_route.ts @@ -44,7 +44,7 @@ export const registerUpdateRoute = ({ }, }, license.guardApiRoute(async (context, request, response) => { - const { client } = context.core.elasticsearch; + const { client } = (await context.core).elasticsearch; const { id } = request.params; // We need to first pause the follower and then resume it by passing the advanced settings diff --git a/x-pack/plugins/data_enhanced/server/routes/session.test.ts b/x-pack/plugins/data_enhanced/server/routes/session.test.ts index 507bb173baf6..c6625a349188 100644 --- a/x-pack/plugins/data_enhanced/server/routes/session.test.ts +++ b/x-pack/plugins/data_enhanced/server/routes/session.test.ts @@ -9,10 +9,7 @@ import type { MockedKeys } from '@kbn/utility-types/jest'; import type { CoreSetup, Logger } from '@kbn/core/server'; import { coreMock, httpServerMock } from '@kbn/core/server/mocks'; -import type { - PluginStart as DataPluginStart, - DataRequestHandlerContext, -} from '@kbn/data-plugin/server'; +import type { PluginStart as DataPluginStart } from '@kbn/data-plugin/server'; import { dataPluginMock } from '@kbn/data-plugin/server/mocks'; import { registerSessionRoutes } from './session'; @@ -25,7 +22,7 @@ enum PostHandlerIndex { describe('registerSessionRoutes', () => { let mockCoreSetup: MockedKeys>; - let mockContext: jest.Mocked; + let mockContext: ReturnType; let mockLogger: Logger; beforeEach(() => { @@ -46,7 +43,7 @@ describe('registerSessionRoutes', () => { const mockRouter = mockCoreSetup.http.createRouter.mock.results[0].value; const [, saveHandler] = mockRouter.post.mock.calls[PostHandlerIndex.SAVE]; - saveHandler(mockContext, mockRequest, mockResponse); + await saveHandler(mockContext, mockRequest, mockResponse); expect(mockContext.search!.saveSession).toHaveBeenCalledWith(sessionId, { name }); }); @@ -61,7 +58,7 @@ describe('registerSessionRoutes', () => { const mockRouter = mockCoreSetup.http.createRouter.mock.results[0].value; const [[, getHandler]] = mockRouter.get.mock.calls; - getHandler(mockContext, mockRequest, mockResponse); + await getHandler(mockContext, mockRequest, mockResponse); expect(mockContext.search!.getSession).toHaveBeenCalledWith(id); }); @@ -80,7 +77,7 @@ describe('registerSessionRoutes', () => { const mockRouter = mockCoreSetup.http.createRouter.mock.results[0].value; const [, findHandler] = mockRouter.post.mock.calls[PostHandlerIndex.FIND]; - findHandler(mockContext, mockRequest, mockResponse); + await findHandler(mockContext, mockRequest, mockResponse); expect(mockContext.search!.findSessions).toHaveBeenCalledWith(body); }); @@ -98,7 +95,7 @@ describe('registerSessionRoutes', () => { const mockRouter = mockCoreSetup.http.createRouter.mock.results[0].value; const [, updateHandler] = mockRouter.put.mock.calls[0]; - updateHandler(mockContext, mockRequest, mockResponse); + await updateHandler(mockContext, mockRequest, mockResponse); expect(mockContext.search!.updateSession).toHaveBeenCalledWith(id, body); }); @@ -113,7 +110,7 @@ describe('registerSessionRoutes', () => { const mockRouter = mockCoreSetup.http.createRouter.mock.results[0].value; const [, cancelHandler] = mockRouter.post.mock.calls[PostHandlerIndex.CANCEL]; - cancelHandler(mockContext, mockRequest, mockResponse); + await cancelHandler(mockContext, mockRequest, mockResponse); expect(mockContext.search!.cancelSession).toHaveBeenCalledWith(id); }); @@ -145,7 +142,7 @@ describe('registerSessionRoutes', () => { const mockRouter = mockCoreSetup.http.createRouter.mock.results[0].value; const [, extendHandler] = mockRouter.post.mock.calls[PostHandlerIndex.EXTEND]; - extendHandler(mockContext, mockRequest, mockResponse); + await extendHandler(mockContext, mockRequest, mockResponse); expect(mockContext.search.extendSession).toHaveBeenCalledWith(id, new Date(expires)); }); diff --git a/x-pack/plugins/data_enhanced/server/routes/session.ts b/x-pack/plugins/data_enhanced/server/routes/session.ts index 399c9b236b35..a0eea258c4db 100644 --- a/x-pack/plugins/data_enhanced/server/routes/session.ts +++ b/x-pack/plugins/data_enhanced/server/routes/session.ts @@ -36,7 +36,8 @@ export function registerSessionRoutes(router: DataEnhancedPluginRouter, logger: request.body; try { - const response = await context.search!.saveSession(sessionId, { + const searchContext = await context.search; + const response = await searchContext.saveSession(sessionId, { name, appId, expires, @@ -70,7 +71,8 @@ export function registerSessionRoutes(router: DataEnhancedPluginRouter, logger: async (context, request, res) => { const { id } = request.params; try { - const response = await context.search!.getSession(id); + const searchContext = await context.search; + const response = await searchContext!.getSession(id); return res.ok({ body: response, @@ -104,7 +106,8 @@ export function registerSessionRoutes(router: DataEnhancedPluginRouter, logger: async (context, request, res) => { const { page, perPage, sortField, sortOrder, filter, searchFields, search } = request.body; try { - const response = await context.search!.findSessions({ + const searchContext = await context.search; + const response = await searchContext!.findSessions({ page, perPage, sortField, @@ -139,7 +142,8 @@ export function registerSessionRoutes(router: DataEnhancedPluginRouter, logger: async (context, request, res) => { const { id } = request.params; try { - await context.search!.deleteSession(id); + const searchContext = await context.search; + await searchContext.deleteSession(id); return res.ok(); } catch (e) { @@ -165,7 +169,8 @@ export function registerSessionRoutes(router: DataEnhancedPluginRouter, logger: async (context, request, res) => { const { id } = request.params; try { - await context.search!.cancelSession(id); + const searchContext = await context.search; + await searchContext.cancelSession(id); return res.ok(); } catch (e) { @@ -196,7 +201,8 @@ export function registerSessionRoutes(router: DataEnhancedPluginRouter, logger: const { id } = request.params; const { name, expires } = request.body; try { - const response = await context.search!.updateSession(id, { name, expires }); + const searchContext = await context.search; + const response = await searchContext.updateSession(id, { name, expires }); return res.ok({ body: response, @@ -227,7 +233,8 @@ export function registerSessionRoutes(router: DataEnhancedPluginRouter, logger: const { id } = request.params; const { expires } = request.body; try { - const response = await context.search!.extendSession(id, new Date(expires)); + const searchContext = await context.search; + const response = await searchContext.extendSession(id, new Date(expires)); return res.ok({ body: response, diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/automated_curation_history.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/automated_curation_history.test.tsx index cd8ba123b584..7c05a3a6b0f0 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/automated_curation_history.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/automated_curation_history.test.tsx @@ -9,8 +9,14 @@ import React from 'react'; import { shallow } from 'enzyme'; +import { EuiButtonEmpty } from '@elastic/eui'; + +import { nextTick } from '@kbn/test-jest-helpers'; + import { EntSearchLogStream } from '../../../../shared/log_stream'; +import { DataPanel } from '../../data_panel'; + import { AutomatedCurationHistory } from './automated_curation_history'; describe('AutomatedCurationHistory', () => { @@ -20,4 +26,17 @@ describe('AutomatedCurationHistory', () => { 'appsearch.adaptive_relevance.query: some text and event.kind: event and event.dataset: search-relevance-suggestions and appsearch.adaptive_relevance.engine: foo and event.action: curation_suggestion and appsearch.adaptive_relevance.suggestion.new_status: automated' ); }); + + it('sets new endTimestamp when refresh is pressed', async () => { + const wrapper = shallow(); + const initialTimestamp = wrapper.find(EntSearchLogStream).prop('endTimestamp'); + await nextTick(); + + // Find the refresh button and click + shallow(wrapper.find(DataPanel).prop('title')).find(EuiButtonEmpty).simulate('click'); + + wrapper.update(); + + expect(wrapper.find(EntSearchLogStream).prop('endTimestamp')).not.toEqual(initialTimestamp); + }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_history/components/automated_curations_history_panel.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_history/components/automated_curations_history_panel.test.tsx index 36703dc0d0d8..8e2f3e8eff0a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_history/components/automated_curations_history_panel.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_history/components/automated_curations_history_panel.test.tsx @@ -13,6 +13,10 @@ import React from 'react'; import { shallow } from 'enzyme'; +import { EuiButtonEmpty } from '@elastic/eui'; + +import { nextTick } from '@kbn/test-jest-helpers'; + import { EntSearchLogStream } from '../../../../../../shared/log_stream'; import { DataPanel } from '../../../../data_panel'; @@ -32,4 +36,17 @@ describe('AutomatedCurationsHistoryPanel', () => { 'event.kind: event and event.dataset: search-relevance-suggestions and appsearch.adaptive_relevance.engine: some-engine and event.action: curation_suggestion and appsearch.adaptive_relevance.suggestion.new_status: automated' ); }); + + it('sets new endTimestamp when refresh is pressed', async () => { + const wrapper = shallow(); + const initialTimestamp = wrapper.find(EntSearchLogStream).prop('endTimestamp'); + await nextTick(); + + // Find the refresh button and click + shallow(wrapper.find(DataPanel).prop('title')).find(EuiButtonEmpty).simulate('click'); + + wrapper.update(); + + expect(wrapper.find(EntSearchLogStream).prop('endTimestamp')).not.toEqual(initialTimestamp); + }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_history/components/ignored_queries_panel/ignored_queries_panel.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_history/components/ignored_queries_panel/ignored_queries_panel.test.tsx index 8c1b4212040a..e9412966d00e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_history/components/ignored_queries_panel/ignored_queries_panel.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_history/components/ignored_queries_panel/ignored_queries_panel.test.tsx @@ -15,7 +15,9 @@ import React from 'react'; import { shallow } from 'enzyme'; -import { EuiBasicTable } from '@elastic/eui'; +import { EuiBasicTable, EuiButtonEmpty } from '@elastic/eui'; + +import { DataPanel } from '../../../../../data_panel'; import { IgnoredQueriesPanel } from './ignored_queries_panel'; @@ -91,4 +93,13 @@ describe('IgnoredQueriesPanel', () => { expect(mockActions.onPaginate).toHaveBeenCalledWith(1); }); + + it('fetches data on refresh button press', () => { + const wrapper = shallow(); + expect(mockActions.loadIgnoredQueries).toHaveBeenCalledTimes(1); + + shallow(wrapper.find(DataPanel).prop('title')).find(EuiButtonEmpty).simulate('click'); + + expect(mockActions.loadIgnoredQueries).toHaveBeenCalledTimes(2); + }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/elasticsearch_card/elasticsearch_card.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/elasticsearch_card/elasticsearch_card.tsx index 0283249ac289..fd84267ebb8e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/elasticsearch_card/elasticsearch_card.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/elasticsearch_card/elasticsearch_card.tsx @@ -11,9 +11,9 @@ import { EuiPanel, EuiFlexGroup, EuiFlexItem, EuiText, EuiTitle, EuiSpacer } fro import { i18n } from '@kbn/i18n'; +import { ElasticsearchResources } from '../../../shared/elasticsearch_resources'; import { EuiButtonTo } from '../../../shared/react_router_helpers'; import { ELASTICSEARCH_GUIDE_PATH } from '../../routes'; -import { ElasticsearchResources } from '../elasticsearch_resources'; export const ElasticsearchCard: React.FC = () => { return ( diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/elasticsearch_guide/elasticsearch_guide.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/elasticsearch_guide/elasticsearch_guide.tsx index fb1bc7a2820e..d722cfd637cc 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/elasticsearch_guide/elasticsearch_guide.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/elasticsearch_guide/elasticsearch_guide.tsx @@ -24,10 +24,10 @@ import { import { i18n } from '@kbn/i18n'; import { docLinks } from '../../../shared/doc_links'; +import { ElasticsearchResources } from '../../../shared/elasticsearch_resources'; import { ElasticsearchClientInstructions } from '../elasticsearch_client_instructions'; import { ElasticsearchCloudId } from '../elasticsearch_cloud_id'; -import { ElasticsearchResources } from '../elasticsearch_resources'; // Replace FormattedMessage with i18n strings diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/layout/index.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/layout/index.ts index 09a1e910fbbd..ea69823f82b8 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/layout/index.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/layout/index.ts @@ -7,3 +7,4 @@ export { useEnterpriseSearchOverviewNav } from './nav'; export { EnterpriseSearchOverviewPageTemplate } from './page_template'; +export { EnterpriseSearchOverviewHeaderActions } from './kibana_header_actions'; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/layout/kibana_header_actions.test.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/layout/kibana_header_actions.test.tsx new file mode 100644 index 000000000000..88f5da60e362 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/layout/kibana_header_actions.test.tsx @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { mount } from 'enzyme'; + +import { EuiPopover, EuiButtonEmpty } from '@elastic/eui'; + +import { EnterpriseSearchOverviewHeaderActions } from './kibana_header_actions'; + +describe('Enterprise Search overview HeaderActions', () => { + it('renders', () => { + const wrapper = mount(); + const popover = wrapper.find(EuiPopover); + + expect(popover.find(EuiButtonEmpty).text()).toContain('Deployment details'); + expect(popover.prop('isOpen')).toBeFalsy(); + }); + + it('opens popover when clicked', () => { + const wrapper = mount(); + + expect(wrapper.find(EuiPopover).prop('isOpen')).toBeFalsy(); + wrapper.find(EuiPopover).find(EuiButtonEmpty).simulate('click'); + wrapper.update(); + + expect(wrapper.find(EuiPopover).prop('isOpen')).toBeTruthy(); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/layout/kibana_header_actions.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/layout/kibana_header_actions.tsx new file mode 100644 index 000000000000..0383618ecf87 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/layout/kibana_header_actions.tsx @@ -0,0 +1,115 @@ +/* + * 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 React, { useState } from 'react'; + +import { + EuiHeaderLinks, + EuiIcon, + EuiCopy, + EuiButton, + EuiButtonEmpty, + EuiButtonIcon, + EuiPopover, + EuiFormRow, + EuiFieldText, + EuiFlexGroup, + EuiFlexItem, + EuiSpacer, + EuiPopoverTitle, + EuiPopoverFooter, + EuiText, +} from '@elastic/eui'; + +import { i18n } from '@kbn/i18n'; + +export const EnterpriseSearchOverviewHeaderActions: React.FC = () => { + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + // TODO change it with actual value + // TODO make this conditional only for users on cloud, as on-prem users will not have a deployment id to show. + const clientId = 'fgdshjafghj13eshfdjag718yfhjdskf'; + + return ( + + setIsPopoverOpen(!isPopoverOpen)}> + +   + {i18n.translate('xpack.enterpriseSearch.overview.deploymentDetails', { + defaultMessage: 'Deployment details', + })} + + } + isOpen={isPopoverOpen} + closePopover={() => setIsPopoverOpen(false)} + > + + {i18n.translate('xpack.enterpriseSearch.overview.deploymentDetails', { + defaultMessage: 'Deployment details', + })} + + +

+ {i18n.translate('xpack.enterpriseSearch.overview.deploymentDetails.description', { + defaultMessage: + 'Send data to Elastic from your applications by referencing your deployment and Elasticsearch information.', + })} +

+
+ + + + + + + + + {(copy) => ( + + )} + + + + + + {/* TODO need link to Create and manage API keys*/} + + {i18n.translate('xpack.enterpriseSearch.overview.createAndManageButton', { + defaultMessage: 'Create and manage API keys', + })} + + +
+
+ ); +}; diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/state/index.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/overview_content/index.ts similarity index 71% rename from x-pack/plugins/security_solution/public/management/pages/trusted_apps/state/index.ts rename to x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/overview_content/index.ts index a06fceab29d4..83d1f526c36c 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/state/index.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/overview_content/index.ts @@ -5,5 +5,4 @@ * 2.0. */ -export * from '../../../state/async_resource_state'; -export * from './trusted_apps_list_page_state'; +export { OverviewContent } from './overview_content'; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/overview_content/overview_content.test.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/overview_content/overview_content.test.tsx new file mode 100644 index 000000000000..53c31eaf0498 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/overview_content/overview_content.test.tsx @@ -0,0 +1,73 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { setMockValues } from '../../../__mocks__/kea_logic'; + +import React from 'react'; + +import { shallow } from 'enzyme'; + +import { EuiEmptyPrompt } from '@elastic/eui'; + +import { AddContentEmptyPrompt } from '../../../shared/add_content_empty_prompt'; +import { GettingStartedSteps } from '../../../shared/getting_started_steps'; + +import { LicenseCallout } from '../license_callout'; +import { SetupGuideCta } from '../setup_guide'; +import { TrialCallout } from '../trial_callout'; + +import { OverviewContent } from '.'; + +describe('OverviewContent', () => { + const props = { + access: {}, + isWorkplaceSearchAdmin: true, + }; + + it('renders the overview page, Getting Started steps & setup guide CTAs with no host set', () => { + setMockValues({ config: { host: '' } }); + const wrapper = shallow(); + + expect(wrapper.find(GettingStartedSteps)).toHaveLength(1); + expect(wrapper.find(AddContentEmptyPrompt)).toHaveLength(1); + expect(wrapper.find(SetupGuideCta)).toHaveLength(1); + expect(wrapper.find(LicenseCallout)).toHaveLength(0); + }); + + it('renders the trial callout', () => { + setMockValues({ config: { host: 'localhost' } }); + const wrapper = shallow(); + + expect(wrapper.find(TrialCallout)).toHaveLength(1); + }); + + // TODO Refactor this and other tests according to the search indices permissions + describe('access checks when host is set', () => { + beforeEach(() => { + setMockValues({ config: { host: 'localhost' } }); + }); + + it('renders the license callout when user has access to a product', () => { + setMockValues({ config: { host: 'localhost' } }); + const wrapper = shallow( + + ); + + expect(wrapper.find(LicenseCallout)).toHaveLength(1); + }); + + it('renders empty prompt and overview or license callout if the user does not have access', () => { + const wrapper = shallow(); + + expect(wrapper.find(EuiEmptyPrompt)).toHaveLength(1); + expect(wrapper.find(GettingStartedSteps)).toHaveLength(0); + expect(wrapper.find(AddContentEmptyPrompt)).toHaveLength(0); + expect(wrapper.find(LicenseCallout)).toHaveLength(0); + expect(wrapper.find(SetupGuideCta)).toHaveLength(0); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/overview_content/overview_content.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/overview_content/overview_content.tsx new file mode 100644 index 000000000000..3824a09a0bc1 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/overview_content/overview_content.tsx @@ -0,0 +1,159 @@ +/* + * 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 React from 'react'; + +import { useValues } from 'kea'; + +import { + EuiButton, + EuiEmptyPrompt, + EuiFlexGroup, + EuiFlexItem, + EuiImage, + EuiLink, + EuiPageBody, + EuiSpacer, + EuiTitle, +} from '@elastic/eui'; +import { Chat } from '@kbn/cloud-plugin/public'; +import { i18n } from '@kbn/i18n'; + +import { AddContentEmptyPrompt } from '../../../shared/add_content_empty_prompt'; +import { docLinks } from '../../../shared/doc_links'; +import { ElasticsearchResources } from '../../../shared/elasticsearch_resources'; +import { GettingStartedSteps } from '../../../shared/getting_started_steps'; +import { KibanaLogic } from '../../../shared/kibana'; +import { SetEnterpriseSearchChrome as SetPageChrome } from '../../../shared/kibana_chrome'; +import { SendEnterpriseSearchTelemetry as SendTelemetry } from '../../../shared/telemetry'; + +import { EnterpriseSearchOverviewPageTemplate } from '../layout'; +import { LicenseCallout } from '../license_callout'; +import illustration from '../product_selector/lock_light.svg'; +import { SetupGuideCta } from '../setup_guide'; + +import { TrialCallout } from '../trial_callout'; + +interface OverviewContentProps { + access: { + hasAppSearchAccess?: boolean; + hasWorkplaceSearchAccess?: boolean; + }; + isWorkplaceSearchAdmin: boolean; +} + +export const OverviewContent: React.FC = ({ access }) => { + const { hasAppSearchAccess, hasWorkplaceSearchAccess } = access; + const { config } = useValues(KibanaLogic); + + // TODO Refactor this and the tests according to the search indices permissions + // If Enterprise Search has been set up and the user does not have access to either product, show a message saying they + // need to contact an administrator to get access to one of the products. + const shouldShowEnterpriseSearchOverview = + !config.host || hasAppSearchAccess || hasWorkplaceSearchAccess; + + const enterpriseSearchOverview = ( + <> + + + + + + + + + + + + + {config.host ? : } + + ); + + const insufficientAccessMessage = ( + + } + title={ +

+ {i18n.translate('xpack.enterpriseSearch.overviewContent.insufficientPermissionsTitle', { + defaultMessage: 'Insufficient permissions', + })} +

+ } + layout="horizontal" + color="plain" + body={ + <> +

+ {i18n.translate('xpack.enterpriseSearch.overviewContent.insufficientPermissionsBody', { + defaultMessage: + 'You don’t have access to view this page. If you feel this may be an error, please contact your administrator.', + })} +

+ + } + actions={ + + {i18n.translate( + 'xpack.enterpriseSearch.overviewContent.insufficientPermissionsButtonLabel', + { + defaultMessage: 'Go to the Kibana dashboard', + } + )} + + } + footer={ + <> + + + {i18n.translate( + 'xpack.enterpriseSearch.overviewContent.insufficientPermissionsFooterBody', + { + defaultMessage: 'Go to the Kibana dashboard', + } + )} + + {' '} + + {i18n.translate( + 'xpack.enterpriseSearch.overviewContent.insufficientPermissionsFooterLinkLabel', + { + defaultMessage: 'Read documentation', + } + )} + + + } + /> + ); + return ( + + + + + + {shouldShowEnterpriseSearchOverview ? enterpriseSearchOverview : insufficientAccessMessage} + + + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/add_content_empty_prompt/add_content_empty_prompt.scss b/x-pack/plugins/enterprise_search/public/applications/shared/add_content_empty_prompt/add_content_empty_prompt.scss new file mode 100644 index 000000000000..c0fafb151174 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/add_content_empty_prompt/add_content_empty_prompt.scss @@ -0,0 +1,7 @@ +.addContentEmptyPrompt { + @include euiBreakpoint('xs','s') { + flex-direction: column-reverse; + } + + border-bottom: $euiBorderThin; +} diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/add_content_empty_prompt/add_content_empty_prompt.test.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/add_content_empty_prompt/add_content_empty_prompt.test.tsx new file mode 100644 index 000000000000..b9e23f00f06b --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/add_content_empty_prompt/add_content_empty_prompt.test.tsx @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { shallow, ShallowWrapper } from 'enzyme'; + +import { EuiLinkTo } from '../react_router_helpers'; + +import { AddContentEmptyPrompt } from '.'; + +describe('AddContentEmptyPrompt', () => { + let wrapper: ShallowWrapper; + + beforeAll(() => { + wrapper = shallow(); + }); + + it('renders', () => { + expect(wrapper.find('h2').text()).toEqual('Add content to Enterprise Search'); + expect(wrapper.find(EuiLinkTo).prop('to')).toEqual( + '/app/enterprise_search/content/search_indices' + ); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/add_content_empty_prompt/add_content_empty_prompt.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/add_content_empty_prompt/add_content_empty_prompt.tsx new file mode 100644 index 000000000000..0679739a7683 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/add_content_empty_prompt/add_content_empty_prompt.tsx @@ -0,0 +1,96 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { generatePath } from 'react-router-dom'; + +import { + EuiImage, + EuiFlexGroup, + EuiFlexItem, + EuiButton, + EuiLink, + EuiPanel, + EuiTitle, + EuiText, + EuiSpacer, +} from '@elastic/eui'; + +import { i18n } from '@kbn/i18n'; + +import { ENTERPRISE_SEARCH_CONTENT_PLUGIN } from '../../../../common/constants'; +import { SEARCH_INDICES_PATH } from '../../enterprise_search_content/routes'; +import { EuiLinkTo } from '../react_router_helpers'; + +import searchIndicesIllustration from './search_indices.svg'; +import './add_content_empty_prompt.scss'; + +export const AddContentEmptyPrompt: React.FC = () => { + return ( + + + + + + +

+ {i18n.translate('xpack.enterpriseSearch.overview.emptyState.heading', { + defaultMessage: 'Add content to Enterprise Search', + })} +

+
+ + +

+ {i18n.translate('xpack.enterpriseSearch.emptyState.description', { + defaultMessage: + "Data you add in Enterprise Search is called a Search index and it's searchable in both App and Workplace Search. Now you can use your connectors in App Search and your web crawlers in Workplace Search.", + })} +

+
+
+ + + + + + {i18n.translate('xpack.enterpriseSearch.overview.emptyState.buttonTitle', { + defaultMessage: 'Add content to Enterprise Search', + })} + + + + + {/* TODO need link for Learn More link*/} + + {i18n.translate('xpack.enterpriseSearch.overview.emptyState.footerLinkTitle', { + defaultMessage: 'Learn more', + })} + + + + +
+
+ + + +
+
+ ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/add_content_empty_prompt/index.ts b/x-pack/plugins/enterprise_search/public/applications/shared/add_content_empty_prompt/index.ts new file mode 100644 index 000000000000..1ae85c30a2f5 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/add_content_empty_prompt/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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { AddContentEmptyPrompt } from './add_content_empty_prompt'; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/search_indices.svg b/x-pack/plugins/enterprise_search/public/applications/shared/add_content_empty_prompt/search_indices.svg similarity index 100% rename from x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/search_indices.svg rename to x-pack/plugins/enterprise_search/public/applications/shared/add_content_empty_prompt/search_indices.svg diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/elasticsearch_resources/elasticsearch_resources.test.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/elasticsearch_resources/elasticsearch_resources.test.tsx new file mode 100644 index 000000000000..3dd3b1c8ddfa --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/elasticsearch_resources/elasticsearch_resources.test.tsx @@ -0,0 +1,47 @@ +/* + * 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. + */ + +jest.mock('../doc_links', () => ({ + docLinks: { + elasticsearchGettingStarted: 'elasticsearchGettingStarted-link', + elasticsearchCreateIndex: 'elasticsearchCreateIndex-link', + clientsGuide: 'elasticsearchClientsGuide-link', + }, +})); +import React from 'react'; + +import { shallow, ShallowWrapper } from 'enzyme'; + +import { EuiLink } from '@elastic/eui'; + +import { ElasticsearchResources } from '.'; + +describe('ElasticsearchResources', () => { + let wrapper: ShallowWrapper; + + beforeAll(() => { + wrapper = shallow(); + }); + + it('renders', () => { + expect(wrapper.find('h4').text()).toEqual('Resources'); + + expect(wrapper.find(EuiLink).at(0).prop('href')).toEqual('elasticsearchGettingStarted-link'); + expect(wrapper.find(EuiLink).at(0).text()).toEqual('Getting started with Elasticsearch'); + + expect(wrapper.find(EuiLink).at(1).prop('href')).toEqual('elasticsearchCreateIndex-link'); + expect(wrapper.find(EuiLink).at(1).text()).toEqual('Create a new index'); + + expect(wrapper.find(EuiLink).at(2).prop('href')).toEqual('elasticsearchClientsGuide-link'); + expect(wrapper.find(EuiLink).at(2).text()).toEqual('Setup a language client'); + + expect(wrapper.find(EuiLink).at(3).prop('href')).toEqual( + 'https://github.com/elastic/search-ui/tree/master/packages/search-ui-elasticsearch-connector' + ); + expect(wrapper.find(EuiLink).at(3).text()).toEqual('Search UI for Elasticsearch'); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/elasticsearch_resources/elasticsearch_resources.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/elasticsearch_resources/elasticsearch_resources.tsx similarity index 92% rename from x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/elasticsearch_resources/elasticsearch_resources.tsx rename to x-pack/plugins/enterprise_search/public/applications/shared/elasticsearch_resources/elasticsearch_resources.tsx index b7d0390f4007..6a274e75179b 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/elasticsearch_resources/elasticsearch_resources.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/elasticsearch_resources/elasticsearch_resources.tsx @@ -10,10 +10,10 @@ import React from 'react'; import { EuiSpacer, EuiPanel, EuiTitle, EuiLink } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { docLinks } from '../../../shared/doc_links'; +import { docLinks } from '../doc_links'; export const ElasticsearchResources: React.FC = () => ( - +

{i18n.translate('xpack.enterpriseSearch.overview.elasticsearchResources.title', { @@ -40,7 +40,7 @@ export const ElasticsearchResources: React.FC = () => ( {i18n.translate( 'xpack.enterpriseSearch.overview.elasticsearchResources.elasticsearchClients', - { defaultMessage: 'Elasticsearch clients' } + { defaultMessage: 'Setup a language client' } )} diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/elasticsearch_resources/index.ts b/x-pack/plugins/enterprise_search/public/applications/shared/elasticsearch_resources/index.ts similarity index 100% rename from x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/elasticsearch_resources/index.ts rename to x-pack/plugins/enterprise_search/public/applications/shared/elasticsearch_resources/index.ts diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/getting_started_steps/getting_started_steps.test.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started_steps/getting_started_steps.test.tsx new file mode 100644 index 000000000000..0595e39475b4 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started_steps/getting_started_steps.test.tsx @@ -0,0 +1,56 @@ +/* + * 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. + */ + +jest.mock('../doc_links', () => ({ + docLinks: { + elasticsearchGettingStarted: 'elasticsearchGettingStarted-link', + elasticsearchCreateIndex: 'elasticsearchCreateIndex-link', + clientsGuide: 'elasticsearchClientsGuide-link', + }, +})); +import React from 'react'; + +import { shallow, ShallowWrapper } from 'enzyme'; + +import { EuiSteps } from '@elastic/eui'; + +import { EuiLinkTo } from '../react_router_helpers'; + +import { IconRow } from './icon_row'; + +import { GettingStartedSteps } from '.'; + +describe('GettingStartedSteps', () => { + let wrapper: ShallowWrapper; + + beforeAll(() => { + wrapper = shallow(); + }); + + it('renders', () => { + const steps = wrapper + .find(EuiSteps) + .prop('steps') + .map(({ title, children, status, ...rest }) => ({ + title, + status, + children: shallow(
{children}
), + ...rest, + })); + + expect(steps[0].title).toEqual('Add your documents and data to Enterprise Search'); + expect(steps[0].status).toEqual('current'); + expect(steps[0].children.find(IconRow).length).toEqual(1); + + expect(steps[1].title).toEqual('Build a search experience'); + expect(steps[1].status).toEqual('incomplete'); + expect(steps[1].children.find(EuiLinkTo).prop('to')).toEqual('/elasticsearch_guide'); + + expect(steps[2].title).toEqual('Tune your search relevance'); + expect(steps[2].status).toEqual('incomplete'); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/getting_started_steps/getting_started_steps.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started_steps/getting_started_steps.tsx new file mode 100644 index 000000000000..e9a3f477c704 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started_steps/getting_started_steps.tsx @@ -0,0 +1,181 @@ +/* + * 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 React, { useState } from 'react'; + +import { + EuiButton, + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiPopover, + EuiSpacer, + EuiSteps, + EuiText, + EuiContextMenuPanel, + EuiContextMenuItem, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +import { ELASTICSEARCH_GUIDE_PATH } from '../../enterprise_search_overview/routes'; + +import { EuiLinkTo } from '../react_router_helpers'; + +import { IconRow } from './icon_row'; + +export const GettingStartedSteps: React.FC = () => { + // TODO replace with logic file + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + + return ( + + + + +

+ {i18n.translate( + 'xpack.enterpriseSearch.overview.gettingStartedSteps.addData.message', + { + defaultMessage: + 'Get started by adding your data to Enterprise Search. You can use the Elastic Web Crawler, API endpoints, existing Elasticsearch indices or third party connectors like Google Drive, Microsoft Sharepoint and more.', + } + )} +

+
+ + + + ), + status: 'current', + }, + { + title: i18n.translate( + 'xpack.enterpriseSearch.overview.gettingStartedSteps.buildSearchExperience.title', + { defaultMessage: 'Build a search experience' } + ), + children: ( + <> + +

+ {i18n.translate( + 'xpack.enterpriseSearch.overview.gettingStartedSteps.buildSearchExperience.message', + { + defaultMessage: + 'You can use Search Engines to build customized search experiences for your customers or internal teams with App Search or Workplace Search. Or you can use Search UI to connect directly to an Elasticsearch index to build client-side search experinces for your users.', + } + )} +

+
+ + + + setIsPopoverOpen(!isPopoverOpen)} + > + {i18n.translate( + 'xpack.enterpriseSearch.overview.gettingStartedSteps.createASearchEngineButton', + { defaultMessage: 'Create a search engine' } + )} + + } + isOpen={isPopoverOpen} + closePopover={() => setIsPopoverOpen(false)} + > + {/* TODO add onclick for these links*/} + {}}> + +

+ {i18n.translate( + 'xpack.enterpriseSearch.overview.gettingStartedSteps.createAppSearchEngine.title', + { defaultMessage: 'Create an App Search engine' } + )} +

+ {i18n.translate( + 'xpack.enterpriseSearch.overview.gettingStartedSteps.createAppSearchEngine.description', + { + defaultMessage: + 'All the power of Elasticsearch, without the learning curve.', + } + )} +
+ , + {}}> + +

+ {i18n.translate( + 'xpack.enterpriseSearch.overview.gettingStartedSteps.createWorkplaceSearchGroup.title', + { defaultMessage: 'Create a Workplace Search group' } + )} +

+ {i18n.translate( + 'xpack.enterpriseSearch.overview.gettingStartedSteps.createWorkplaceSearchGroup.description', + { + defaultMessage: 'A secure search experience for internal teams', + } + )} +
+
, + ]} + /> +
+
+ + + +   + {i18n.translate( + 'xpack.enterpriseSearch.overview.gettingStartedSteps.searchWithElasticsearchLink', + { defaultMessage: 'Search with the Elasticsearch API' } + )} + + +
+ + ), + status: 'incomplete', + }, + { + title: i18n.translate( + 'xpack.enterpriseSearch.overview.gettingStartedSteps.tuneSearchExperience.title', + { defaultMessage: 'Tune your search relevance' } + ), + children: ( + +

+ {i18n.translate( + 'xpack.enterpriseSearch.overview.gettingStartedSteps.tuneSearchExperience.message', + { + defaultMessage: + "Dive into analytics and tune the result settings to help your users find exactly what they're looking for", + } + )} +

+
+ ), + status: 'incomplete', + }, + ]} + /> +
+
+ ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/getting_started_steps/icon_row.scss b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started_steps/icon_row.scss new file mode 100644 index 000000000000..6992cdead280 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started_steps/icon_row.scss @@ -0,0 +1,13 @@ +.gettingStartedSteps { + .grayscaleSvg { + filter: grayscale(1); + } + + .addManyMoreButton { + border: $euiBorderThin; + } + + .iconTooltip { + border-bottom: $euiBorderThin; + } +} diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/getting_started_steps/icon_row.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started_steps/icon_row.tsx new file mode 100644 index 000000000000..8ae50183914d --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started_steps/icon_row.tsx @@ -0,0 +1,115 @@ +/* + * 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 React from 'react'; + +import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiBadge, EuiToolTip, EuiText } from '@elastic/eui'; + +import { i18n } from '@kbn/i18n'; + +import { images } from '../../workplace_search/components/shared/assets/source_icons'; + +import './icon_row.scss'; + +const icons = [ + { + icon: 'logoElasticsearch', + title: 'Elasticsearch', + tooltip: i18n.translate('xpack.enterpriseSearch.overview.iconRow.elasticsearch.tooltip', { + defaultMessage: + 'Use App and Workplace Search Search Engines with existing Elasticsearch indices', + }), + }, + { + icon: 'desktop', + title: i18n.translate('xpack.enterpriseSearch.overview.iconRow.api.title', { + defaultMessage: 'API', + }), + tooltip: i18n.translate('xpack.enterpriseSearch.overview.iconRow.api.tooltip', { + defaultMessage: 'POST documents to an API endpoint from your own applications', + }), + }, + { + icon: 'globe', + title: i18n.translate('xpack.enterpriseSearch.overview.iconRow.crawler.title', { + defaultMessage: 'Elastic Web Crawler', + }), + tooltip: i18n.translate('xpack.enterpriseSearch.overview.iconRow.crawler.tooltip', { + defaultMessage: 'Automatically index content from your websites', + }), + }, + { + icon: images.confluence, + title: i18n.translate('xpack.enterpriseSearch.overview.iconRow.confluence.title', { + defaultMessage: 'Confluence', + }), + tooltip: i18n.translate('xpack.enterpriseSearch.overview.iconRow.confluence.tooltip', { + defaultMessage: 'Index content from Atlassian Confluence', + }), + }, + { + icon: images.googleDrive, + title: i18n.translate('xpack.enterpriseSearch.overview.iconRow.googleDrive.title', { + defaultMessage: 'Google Drive', + }), + tooltip: i18n.translate('xpack.enterpriseSearch.overview.iconRow.googleDrive.tooltip', { + defaultMessage: 'Index documents from Google Drive', + }), + }, + { + icon: images.sharePoint, + title: i18n.translate('xpack.enterpriseSearch.overview.iconRow.sharePoint.title', { + defaultMessage: 'Microsoft SharePoint', + }), + tooltip: i18n.translate('xpack.enterpriseSearch.overview.iconRow.sharePoint.tooltip', { + defaultMessage: 'Index content from Microsoft SharePoint', + }), + }, + { + icon: images.github, + title: i18n.translate('xpack.enterpriseSearch.overview.iconRow.github.title', { + defaultMessage: 'GitHub', + }), + tooltip: i18n.translate('xpack.enterpriseSearch.overview.iconRow.github.tooltip', { + defaultMessage: 'Index issues, pull requests, and more from GitHub', + }), + }, +]; + +export const IconRow: React.FC = () => { + return ( + + + + {icons.map((item, index) => { + return ( + + +

{item.title}

+ {item.tooltip} + + } + > + +
+
+ ); + })} +
+
+ + + {i18n.translate('xpack.enterpriseSearch.overview.iconRow.manyMoreBadge', { + defaultMessage: 'And many more', + })} + + +
+ ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/getting_started_steps/index.ts b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started_steps/index.ts new file mode 100644 index 000000000000..71ad56d84cb0 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started_steps/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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { GettingStartedSteps } from './getting_started_steps'; diff --git a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/indices.ts b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/indices.ts index 28359bc90052..65868e4d1793 100644 --- a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/indices.ts +++ b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/indices.ts @@ -12,7 +12,7 @@ export function registerListRoute({ router }: RouteDependencies) { router.get( { path: '/internal/enterprise_search/indices', validate: false }, async (context, _, response) => { - const { client } = context.core.elasticsearch; + const { client } = (await context.core).elasticsearch; try { const indices = await fetchIndices(client); return response.ok({ diff --git a/x-pack/plugins/event_log/server/routes/find.ts b/x-pack/plugins/event_log/server/routes/find.ts index fd940d0f64ad..4c9ab70bbe3a 100644 --- a/x-pack/plugins/event_log/server/routes/find.ts +++ b/x-pack/plugins/event_log/server/routes/find.ts @@ -38,7 +38,7 @@ export const findRoute = (router: EventLogRouter, systemLogger: Logger) => { if (!context.eventLog) { return res.badRequest({ body: 'RouteHandlerContext is not registered for eventLog' }); } - const eventLogClient = context.eventLog.getEventLogClient(); + const eventLogClient = (await context.eventLog).getEventLogClient(); const { params: { id, type }, query, diff --git a/x-pack/plugins/event_log/server/routes/find_by_ids.ts b/x-pack/plugins/event_log/server/routes/find_by_ids.ts index 85346b1e87be..a33a03c8242c 100644 --- a/x-pack/plugins/event_log/server/routes/find_by_ids.ts +++ b/x-pack/plugins/event_log/server/routes/find_by_ids.ts @@ -44,7 +44,7 @@ export const findByIdsRoute = (router: EventLogRouter, systemLogger: Logger) => if (!context.eventLog) { return res.badRequest({ body: 'RouteHandlerContext is not registered for eventLog' }); } - const eventLogClient = context.eventLog.getEventLogClient(); + const eventLogClient = (await context.eventLog).getEventLogClient(); const { params: { type }, body: { ids, legacyIds }, diff --git a/x-pack/plugins/event_log/server/types.ts b/x-pack/plugins/event_log/server/types.ts index 10a472e802b9..1336245741bd 100644 --- a/x-pack/plugins/event_log/server/types.ts +++ b/x-pack/plugins/event_log/server/types.ts @@ -6,7 +6,7 @@ */ import { schema, TypeOf } from '@kbn/config-schema'; -import type { IRouter, KibanaRequest, RequestHandlerContext } from '@kbn/core/server'; +import type { IRouter, KibanaRequest, CustomRequestHandlerContext } from '@kbn/core/server'; export type { IEvent, IValidatedEvent } from '../generated/schemas'; export { EventSchema, ECS_VERSION } from '../generated/schemas'; @@ -16,6 +16,7 @@ import { AggregateEventsBySavedObjectResult, QueryEventsBySavedObjectResult, } from './es/cluster_client_adapter'; + export type { QueryEventsBySavedObjectResult, AggregateEventsBySavedObjectResult, @@ -79,9 +80,9 @@ export interface EventLogApiRequestHandlerContext { /** * @internal */ -export interface EventLogRequestHandlerContext extends RequestHandlerContext { +export type EventLogRequestHandlerContext = CustomRequestHandlerContext<{ eventLog: EventLogApiRequestHandlerContext; -} +}>; /** * @internal diff --git a/x-pack/plugins/features/server/routes/index.test.ts b/x-pack/plugins/features/server/routes/index.test.ts index 4d61d131cc0a..31cb1358f63f 100644 --- a/x-pack/plugins/features/server/routes/index.test.ts +++ b/x-pack/plugins/features/server/routes/index.test.ts @@ -15,10 +15,9 @@ import { RequestHandler } from '@kbn/core/server'; import { FeatureKibanaPrivileges, KibanaFeatureConfig, SubFeatureConfig } from '../../common'; function createContextMock(licenseType: LicenseType = 'platinum') { - return { - core: coreMock.createRequestHandlerContext(), + return coreMock.createCustomRequestHandlerContext({ licensing: licensingMock.createRequestHandlerContext({ license: { type: licenseType } }), - }; + }); } function createPrivilege(partial: Partial = {}): FeatureKibanaPrivileges { @@ -138,7 +137,7 @@ describe('GET /api/features', () => { it('returns a list of available features, sorted by their configured order', async () => { const mockResponse = httpServerMock.createResponseFactory(); - routeHandler(createContextMock(), { query: {} } as any, mockResponse); + await routeHandler(createContextMock(), { query: {} } as any, mockResponse); expect(mockResponse.ok).toHaveBeenCalledTimes(1); const [call] = mockResponse.ok.mock.calls; @@ -173,7 +172,7 @@ describe('GET /api/features', () => { it(`by default does not return features that arent allowed by current license`, async () => { const mockResponse = httpServerMock.createResponseFactory(); - routeHandler(createContextMock('basic'), { query: {} } as any, mockResponse); + await routeHandler(createContextMock('basic'), { query: {} } as any, mockResponse); expect(mockResponse.ok).toHaveBeenCalledTimes(1); const [call] = mockResponse.ok.mock.calls; @@ -204,7 +203,7 @@ describe('GET /api/features', () => { it(`ignoreValidLicenses=false does not return features that arent allowed by current license`, async () => { const mockResponse = httpServerMock.createResponseFactory(); - routeHandler( + await routeHandler( createContextMock('basic'), { query: { ignoreValidLicenses: false } } as any, mockResponse @@ -239,7 +238,7 @@ describe('GET /api/features', () => { it(`ignoreValidLicenses=true returns features that arent allowed by current license`, async () => { const mockResponse = httpServerMock.createResponseFactory(); - routeHandler( + await routeHandler( createContextMock('basic'), { query: { ignoreValidLicenses: true } } as any, mockResponse diff --git a/x-pack/plugins/features/server/routes/index.ts b/x-pack/plugins/features/server/routes/index.ts index 1f2b833663f1..bb78f07bc56c 100644 --- a/x-pack/plugins/features/server/routes/index.ts +++ b/x-pack/plugins/features/server/routes/index.ts @@ -26,8 +26,8 @@ export function defineRoutes({ router, featureRegistry }: RouteDefinitionParams) query: schema.object({ ignoreValidLicenses: schema.boolean({ defaultValue: false }) }), }, }, - (context, request, response) => { - const currentLicense = context.licensing!.license; + async (context, request, response) => { + const { license: currentLicense } = await context.licensing; const allFeatures = featureRegistry.getAllKibanaFeatures( currentLicense, diff --git a/x-pack/plugins/features/server/types.ts b/x-pack/plugins/features/server/types.ts index 826d4282cc42..99a6c96e4332 100644 --- a/x-pack/plugins/features/server/types.ts +++ b/x-pack/plugins/features/server/types.ts @@ -5,15 +5,15 @@ * 2.0. */ -import type { RequestHandlerContext, IRouter } from '@kbn/core/server'; +import type { CustomRequestHandlerContext, IRouter } from '@kbn/core/server'; import type { LicensingApiRequestHandlerContext } from '@kbn/licensing-plugin/server'; /** * @internal */ -export interface FeaturesRequestHandlerContext extends RequestHandlerContext { +export type FeaturesRequestHandlerContext = CustomRequestHandlerContext<{ licensing: LicensingApiRequestHandlerContext; -} +}>; /** * @internal diff --git a/x-pack/plugins/file_upload/server/routes.ts b/x-pack/plugins/file_upload/server/routes.ts index 0abab34ebef7..76d6443f47f5 100644 --- a/x-pack/plugins/file_upload/server/routes.ts +++ b/x-pack/plugins/file_upload/server/routes.ts @@ -102,11 +102,8 @@ export function fileUploadRoutes(coreSetup: CoreSetup, logge }, async (context, request, response) => { try { - const result = await analyzeFile( - context.core.elasticsearch.client, - request.body, - request.query - ); + const esClient = (await context.core).elasticsearch.client; + const result = await analyzeFile(esClient, request.body, request.query); return response.ok({ body: result }); } catch (e) { return response.customError(wrapError(e)); @@ -142,6 +139,7 @@ export function fileUploadRoutes(coreSetup: CoreSetup, logge try { const { id } = request.query; const { index, data, settings, mappings, ingestPipeline } = request.body; + const esClient = (await context.core).elasticsearch.client; // `id` being `undefined` tells us that this is a new import due to create a new index. // follow-up import calls to just add additional data will include the `id` of the created @@ -151,7 +149,7 @@ export function fileUploadRoutes(coreSetup: CoreSetup, logge } const result = await importData( - context.core.elasticsearch.client, + esClient, id, index, settings, @@ -182,9 +180,8 @@ export function fileUploadRoutes(coreSetup: CoreSetup, logge }, async (context, request, response) => { try { - const indexExists = await context.core.elasticsearch.client.asCurrentUser.indices.exists( - request.body - ); + const esClient = (await context.core).elasticsearch.client; + const indexExists = await esClient.asCurrentUser.indices.exists(request.body); return response.ok({ body: { exists: indexExists } }); } catch (e) { return response.customError(wrapError(e)); @@ -225,8 +222,9 @@ export function fileUploadRoutes(coreSetup: CoreSetup, logge async (context, request, response) => { try { const { index, timeFieldName, query, runtimeMappings } = request.body; + const esClient = (await context.core).elasticsearch.client; const resp = await getTimeFieldRange( - context.core.elasticsearch.client, + esClient, index, timeFieldName, query, diff --git a/x-pack/plugins/fleet/.storybook/context/stubs.tsx b/x-pack/plugins/fleet/.storybook/context/stubs.tsx index 0f4f81b58f95..092ab680b819 100644 --- a/x-pack/plugins/fleet/.storybook/context/stubs.tsx +++ b/x-pack/plugins/fleet/.storybook/context/stubs.tsx @@ -11,6 +11,7 @@ type Stubs = | 'licensing' | 'storage' | 'data' + | 'unifiedSearch' | 'deprecations' | 'fatalErrors' | 'navigation' @@ -23,6 +24,7 @@ export const stubbedStartServices: StubbedStartServices = { licensing: {} as FleetStartServices['licensing'], storage: {} as FleetStartServices['storage'], data: {} as FleetStartServices['data'], + unifiedSearch: {} as FleetStartServices['unifiedSearch'], deprecations: {} as FleetStartServices['deprecations'], fatalErrors: {} as FleetStartServices['fatalErrors'], navigation: {} as FleetStartServices['navigation'], diff --git a/x-pack/plugins/fleet/kibana.json b/x-pack/plugins/fleet/kibana.json index 66301f6b6fb9..0a45b03803fc 100644 --- a/x-pack/plugins/fleet/kibana.json +++ b/x-pack/plugins/fleet/kibana.json @@ -8,7 +8,7 @@ "server": true, "ui": true, "configPath": ["xpack", "fleet"], - "requiredPlugins": ["licensing", "data", "encryptedSavedObjects", "navigation", "customIntegrations", "share", "spaces", "security"], + "requiredPlugins": ["licensing", "data", "encryptedSavedObjects", "navigation", "customIntegrations", "share", "spaces", "security", "unifiedSearch"], "optionalPlugins": ["features", "cloud", "usageCollection", "home", "globalSearch", "telemetry"], "extraPublicDirs": ["common"], "requiredBundles": ["kibanaReact", "cloud", "esUiShared", "infra", "kibanaUtils", "usageCollection", "unifiedSearch"] diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/filter_dataset.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/filter_dataset.tsx index 865c360a47bd..f49d4910ab9f 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/filter_dataset.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/filter_dataset.tsx @@ -17,7 +17,7 @@ export const DatasetFilter: React.FunctionComponent<{ selectedDatasets: string[]; onToggleDataset: (dataset: string) => void; }> = memo(({ selectedDatasets, onToggleDataset }) => { - const { data } = useStartServices(); + const { unifiedSearch } = useStartServices(); const [isOpen, setIsOpen] = useState(false); const [isLoading, setIsLoading] = useState(false); const [datasetValues, setDatasetValues] = useState([AGENT_DATASET]); @@ -29,7 +29,7 @@ export const DatasetFilter: React.FunctionComponent<{ const fetchValues = async () => { setIsLoading(true); try { - const values = await data.autocomplete.getValueSuggestions({ + const values = await unifiedSearch.autocomplete.getValueSuggestions({ indexPattern: { title: AGENT_LOG_INDEX_PATTERN, fields: [DATASET_FIELD], @@ -44,7 +44,7 @@ export const DatasetFilter: React.FunctionComponent<{ setIsLoading(false); }; fetchValues(); - }, [data.autocomplete]); + }, [unifiedSearch.autocomplete]); return ( void; }> = memo(({ selectedLevels, onToggleLevel }) => { - const { data } = useStartServices(); + const { unifiedSearch } = useStartServices(); const [isOpen, setIsOpen] = useState(false); const [isLoading, setIsLoading] = useState(false); const [levelValues, setLevelValues] = useState([]); @@ -40,7 +40,7 @@ export const LogLevelFilter: React.FunctionComponent<{ const fetchValues = async () => { setIsLoading(true); try { - const values: string[] = await data.autocomplete.getValueSuggestions({ + const values: string[] = await unifiedSearch.autocomplete.getValueSuggestions({ indexPattern: { title: AGENT_LOG_INDEX_PATTERN, fields: [LOG_LEVEL_FIELD], @@ -55,7 +55,7 @@ export const LogLevelFilter: React.FunctionComponent<{ setIsLoading(false); }; fetchValues(); - }, [data.autocomplete]); + }, [unifiedSearch.autocomplete]); const noLogsFound = (
diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/logstash_instructions/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/logstash_instructions/index.tsx index c3d43f85038c..752038e953b1 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/logstash_instructions/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/logstash_instructions/index.tsx @@ -111,10 +111,11 @@ const LogstashInstructionSteps = () => {
API Key
{logstashApiKey.apiKey} - - {(copy) => ( -
-
+ +
+
+ + {(copy) => ( { } )} /> -
-
- )} - + )} + +
+
) : ( { return { licensing: licensingMock.createStart(), data: dataPluginMock.createStartContract(), + unifiedSearch: unifiedSearchPluginMock.createStartContract(), navigation: navigationPluginMock.createStartContract(), customIntegrations: customIntegrationsMock.createStart(), share: sharePluginMock.createStartContract(), diff --git a/x-pack/plugins/fleet/public/plugin.ts b/x-pack/plugins/fleet/public/plugin.ts index b84562098fc7..c94f2ce139ab 100644 --- a/x-pack/plugins/fleet/public/plugin.ts +++ b/x-pack/plugins/fleet/public/plugin.ts @@ -41,6 +41,8 @@ import type { LicensingPluginStart } from '@kbn/licensing-plugin/public'; import type { CloudSetup } from '@kbn/cloud-plugin/public'; import type { GlobalSearchPluginSetup } from '@kbn/global-search-plugin/public'; +import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; + import { PLUGIN_ID, INTEGRATIONS_PLUGIN_ID, @@ -92,6 +94,7 @@ export interface FleetSetupDeps { export interface FleetStartDeps { licensing: LicensingPluginStart; data: DataPublicPluginStart; + unifiedSearch: UnifiedSearchPublicPluginStart; navigation: NavigationPublicPluginStart; customIntegrations: CustomIntegrationsStart; share: SharePluginStart; diff --git a/x-pack/plugins/fleet/server/mocks/index.ts b/x-pack/plugins/fleet/server/mocks/index.ts index 8fd23dfbbac8..01fe5b48f9e3 100644 --- a/x-pack/plugins/fleet/server/mocks/index.ts +++ b/x-pack/plugins/fleet/server/mocks/index.ts @@ -75,7 +75,7 @@ export const createAppContextStartContractMock = ( }; export const createFleetRequestHandlerContextMock = (): jest.Mocked< - FleetRequestHandlerContext['fleet'] + Awaited > => { return { authz: createFleetAuthzMock(), diff --git a/x-pack/plugins/fleet/server/plugin.ts b/x-pack/plugins/fleet/server/plugin.ts index 20d55409e50d..c9e3b9388087 100644 --- a/x-pack/plugins/fleet/server/plugin.ts +++ b/x-pack/plugins/fleet/server/plugin.ts @@ -319,12 +319,11 @@ export class FleetPlugin PLUGIN_ID, async (context, request) => { const plugin = this; + const esClient = (await context.core).elasticsearch.client; return { get agentClient() { - const agentService = plugin.setupAgentService( - context.core.elasticsearch.client.asInternalUser - ); + const agentService = plugin.setupAgentService(esClient.asInternalUser); return { asCurrentUser: agentService.asScoped(request), @@ -382,6 +381,7 @@ export class FleetPlugin this.telemetryEventsSender.setup(deps.telemetry); } + public start(core: CoreStart, plugins: FleetStartDeps): FleetStartContract { appContextService.start({ elasticsearch: core.elasticsearch, diff --git a/x-pack/plugins/fleet/server/routes/agent/actions_handlers.ts b/x-pack/plugins/fleet/server/routes/agent/actions_handlers.ts index 265fe4d9e422..4f3cad9edab2 100644 --- a/x-pack/plugins/fleet/server/routes/agent/actions_handlers.ts +++ b/x-pack/plugins/fleet/server/routes/agent/actions_handlers.ts @@ -24,7 +24,7 @@ export const postNewAgentActionHandlerBuilder = function ( > { return async (context, request, response) => { try { - const esClient = context.core.elasticsearch.client.asInternalUser; + const esClient = (await context.core).elasticsearch.client.asInternalUser; const agent = await actionsService.getAgent(esClient, request.params.agentId); diff --git a/x-pack/plugins/fleet/server/routes/agent/handlers.ts b/x-pack/plugins/fleet/server/routes/agent/handlers.ts index b0c6a975dfea..b9e82e844e81 100644 --- a/x-pack/plugins/fleet/server/routes/agent/handlers.ts +++ b/x-pack/plugins/fleet/server/routes/agent/handlers.ts @@ -32,8 +32,9 @@ import * as AgentService from '../../services/agents'; export const getAgentHandler: RequestHandler< TypeOf > = async (context, request, response) => { - const soClient = context.core.savedObjects.client; - const esClient = context.core.elasticsearch.client.asInternalUser; + const coreContext = await context.core; + const soClient = coreContext.savedObjects.client; + const esClient = coreContext.elasticsearch.client.asInternalUser; try { const body: GetOneAgentResponse = { @@ -55,7 +56,8 @@ export const getAgentHandler: RequestHandler< export const deleteAgentHandler: RequestHandler< TypeOf > = async (context, request, response) => { - const esClient = context.core.elasticsearch.client.asInternalUser; + const coreContext = await context.core; + const esClient = coreContext.elasticsearch.client.asInternalUser; try { await AgentService.deleteAgent(esClient, request.params.agentId); @@ -82,7 +84,8 @@ export const updateAgentHandler: RequestHandler< undefined, TypeOf > = async (context, request, response) => { - const esClient = context.core.elasticsearch.client.asInternalUser; + const coreContext = await context.core; + const esClient = coreContext.elasticsearch.client.asInternalUser; try { await AgentService.updateAgent(esClient, request.params.agentId, { @@ -108,7 +111,8 @@ export const getAgentsHandler: RequestHandler< undefined, TypeOf > = async (context, request, response) => { - const esClient = context.core.elasticsearch.client.asInternalUser; + const coreContext = await context.core; + const esClient = coreContext.elasticsearch.client.asInternalUser; try { const { agents, total, page, perPage } = await AgentService.getAgentsByKuery(esClient, { @@ -143,8 +147,9 @@ export const putAgentsReassignHandler: RequestHandler< undefined, TypeOf > = async (context, request, response) => { - const soClient = context.core.savedObjects.client; - const esClient = context.core.elasticsearch.client.asInternalUser; + const coreContext = await context.core; + const soClient = coreContext.savedObjects.client; + const esClient = coreContext.elasticsearch.client.asInternalUser; try { await AgentService.reassignAgent( soClient, @@ -172,8 +177,9 @@ export const postBulkAgentsReassignHandler: RequestHandler< }); } - const soClient = context.core.savedObjects.client; - const esClient = context.core.elasticsearch.client.asInternalUser; + const coreContext = await context.core; + const soClient = coreContext.savedObjects.client; + const esClient = coreContext.elasticsearch.client.asInternalUser; const agentOptions = Array.isArray(request.body.agents) ? { agentIds: request.body.agents } : { kuery: request.body.agents }; @@ -204,7 +210,8 @@ export const getAgentStatusForAgentPolicyHandler: RequestHandler< undefined, TypeOf > = async (context, request, response) => { - const esClient = context.core.elasticsearch.client.asInternalUser; + const coreContext = await context.core; + const esClient = coreContext.elasticsearch.client.asInternalUser; try { const results = await AgentService.getAgentStatusForAgentPolicy( esClient, @@ -224,7 +231,8 @@ export const getAgentDataHandler: RequestHandler< undefined, TypeOf > = async (context, request, response) => { - const esClient = context.core.elasticsearch.client.asCurrentUser; + const coreContext = await context.core; + const esClient = coreContext.elasticsearch.client.asCurrentUser; try { let items; diff --git a/x-pack/plugins/fleet/server/routes/agent/unenroll_handler.ts b/x-pack/plugins/fleet/server/routes/agent/unenroll_handler.ts index 57752ae5caf6..f9d27ff71ed9 100644 --- a/x-pack/plugins/fleet/server/routes/agent/unenroll_handler.ts +++ b/x-pack/plugins/fleet/server/routes/agent/unenroll_handler.ts @@ -25,8 +25,9 @@ export const postAgentUnenrollHandler: RequestHandler< undefined, TypeOf > = async (context, request, response) => { - const soClient = context.core.savedObjects.client; - const esClient = context.core.elasticsearch.client.asInternalUser; + const coreContext = await context.core; + const soClient = coreContext.savedObjects.client; + const esClient = coreContext.elasticsearch.client.asInternalUser; try { await AgentService.unenrollAgent(soClient, esClient, request.params.agentId, { force: request.body?.force, @@ -52,8 +53,9 @@ export const postBulkAgentsUnenrollHandler: RequestHandler< }); } - const soClient = context.core.savedObjects.client; - const esClient = context.core.elasticsearch.client.asInternalUser; + const coreContext = await context.core; + const soClient = coreContext.savedObjects.client; + const esClient = coreContext.elasticsearch.client.asInternalUser; const agentOptions = Array.isArray(request.body.agents) ? { agentIds: request.body.agents } : { kuery: request.body.agents }; diff --git a/x-pack/plugins/fleet/server/routes/agent/upgrade_handler.ts b/x-pack/plugins/fleet/server/routes/agent/upgrade_handler.ts index dd3375257469..13df9222c952 100644 --- a/x-pack/plugins/fleet/server/routes/agent/upgrade_handler.ts +++ b/x-pack/plugins/fleet/server/routes/agent/upgrade_handler.ts @@ -22,8 +22,9 @@ export const postAgentUpgradeHandler: RequestHandler< undefined, TypeOf > = async (context, request, response) => { - const soClient = context.core.savedObjects.client; - const esClient = context.core.elasticsearch.client.asInternalUser; + const coreContext = await context.core; + const soClient = coreContext.savedObjects.client; + const esClient = coreContext.elasticsearch.client.asInternalUser; const { version, source_uri: sourceUri, force } = request.body; const kibanaVersion = appContextService.getKibanaVersion(); try { @@ -77,8 +78,9 @@ export const postBulkAgentsUpgradeHandler: RequestHandler< undefined, TypeOf > = async (context, request, response) => { - const soClient = context.core.savedObjects.client; - const esClient = context.core.elasticsearch.client.asInternalUser; + const coreContext = await context.core; + const soClient = coreContext.savedObjects.client; + const esClient = coreContext.elasticsearch.client.asInternalUser; const { version, source_uri: sourceUri, agents, force } = request.body; const kibanaVersion = appContextService.getKibanaVersion(); try { diff --git a/x-pack/plugins/fleet/server/routes/agent_policy/handlers.ts b/x-pack/plugins/fleet/server/routes/agent_policy/handlers.ts index 34dc29d89e6c..7b6c2dce0ef0 100644 --- a/x-pack/plugins/fleet/server/routes/agent_policy/handlers.ts +++ b/x-pack/plugins/fleet/server/routes/agent_policy/handlers.ts @@ -45,8 +45,10 @@ export const getAgentPoliciesHandler: FleetRequestHandler< undefined, TypeOf > = async (context, request, response) => { - const soClient = context.fleet.epm.internalSoClient; - const esClient = context.core.elasticsearch.client.asInternalUser; + const coreContext = await context.core; + const fleetContext = await context.fleet; + const soClient = fleetContext.epm.internalSoClient; + const esClient = coreContext.elasticsearch.client.asInternalUser; const { full: withPackagePolicies = false, ...restOfQuery } = request.query; try { const { items, total, page, perPage } = await agentPolicyService.list(soClient, { @@ -81,7 +83,8 @@ export const getAgentPoliciesHandler: FleetRequestHandler< export const getOneAgentPolicyHandler: RequestHandler< TypeOf > = async (context, request, response) => { - const soClient = context.core.savedObjects.client; + const coreContext = await context.core; + const soClient = coreContext.savedObjects.client; try { const agentPolicy = await agentPolicyService.get(soClient, request.params.agentPolicyId); if (agentPolicy) { @@ -107,13 +110,15 @@ export const createAgentPolicyHandler: FleetRequestHandler< TypeOf, TypeOf > = async (context, request, response) => { - const soClient = context.fleet.epm.internalSoClient; - const esClient = context.core.elasticsearch.client.asInternalUser; + const coreContext = await context.core; + const fleetContext = await context.fleet; + const soClient = fleetContext.epm.internalSoClient; + const esClient = coreContext.elasticsearch.client.asInternalUser; const user = (await appContextService.getSecurity()?.authc.getCurrentUser(request)) || undefined; const withSysMonitoring = request.query.sys_monitoring ?? false; const monitoringEnabled = request.body.monitoring_enabled; const { has_fleet_server: hasFleetServer, ...newPolicy } = request.body; - const spaceId = context.fleet.spaceId; + const spaceId = fleetContext.spaceId; try { const body: CreateAgentPolicyResponse = { item: await createAgentPolicyWithPackages({ @@ -141,8 +146,9 @@ export const updateAgentPolicyHandler: RequestHandler< unknown, TypeOf > = async (context, request, response) => { - const soClient = context.core.savedObjects.client; - const esClient = context.core.elasticsearch.client.asInternalUser; + const coreContext = await context.core; + const soClient = coreContext.savedObjects.client; + const esClient = coreContext.elasticsearch.client.asInternalUser; const user = await appContextService.getSecurity()?.authc.getCurrentUser(request); const { force, ...data } = request.body; try { @@ -170,8 +176,9 @@ export const copyAgentPolicyHandler: RequestHandler< unknown, TypeOf > = async (context, request, response) => { - const soClient = context.core.savedObjects.client; - const esClient = context.core.elasticsearch.client.asInternalUser; + const coreContext = await context.core; + const soClient = coreContext.savedObjects.client; + const esClient = coreContext.elasticsearch.client.asInternalUser; const user = await appContextService.getSecurity()?.authc.getCurrentUser(request); try { const agentPolicy = await agentPolicyService.copy( @@ -198,8 +205,9 @@ export const deleteAgentPoliciesHandler: RequestHandler< unknown, TypeOf > = async (context, request, response) => { - const soClient = context.core.savedObjects.client; - const esClient = context.core.elasticsearch.client.asInternalUser; + const coreContext = await context.core; + const soClient = coreContext.savedObjects.client; + const esClient = coreContext.elasticsearch.client.asInternalUser; try { const body: DeleteAgentPolicyResponse = await agentPolicyService.delete( soClient, @@ -218,7 +226,8 @@ export const getFullAgentPolicy: FleetRequestHandler< TypeOf, TypeOf > = async (context, request, response) => { - const soClient = context.fleet.epm.internalSoClient; + const fleetContext = await context.fleet; + const soClient = fleetContext.epm.internalSoClient; if (request.query.kubernetes === true) { try { @@ -273,7 +282,8 @@ export const downloadFullAgentPolicy: FleetRequestHandler< TypeOf, TypeOf > = async (context, request, response) => { - const soClient = context.fleet.epm.internalSoClient; + const fleetContext = await context.fleet; + const soClient = fleetContext.epm.internalSoClient; const { params: { agentPolicyId }, } = request; diff --git a/x-pack/plugins/fleet/server/routes/app/index.ts b/x-pack/plugins/fleet/server/routes/app/index.ts index fa4841c50e5b..9afa422d2511 100644 --- a/x-pack/plugins/fleet/server/routes/app/index.ts +++ b/x-pack/plugins/fleet/server/routes/app/index.ts @@ -28,7 +28,8 @@ export const getCheckPermissionsHandler: FleetRequestHandler< if (!appContextService.getSecurityLicense().isEnabled()) { return response.ok({ body: missingSecurityBody }); } else { - if (!context.fleet.authz.fleet.all) { + const fleetContext = await context.fleet; + if (!fleetContext.authz.fleet.all) { return response.ok({ body: { success: false, @@ -38,7 +39,7 @@ export const getCheckPermissionsHandler: FleetRequestHandler< } // check the manage_service_account cluster privilege else if (request.query.fleetServerSetup) { - const esClient = context.core.elasticsearch.client.asCurrentUser; + const esClient = (await context.core).elasticsearch.client.asCurrentUser; const { has_all_requested: hasAllPrivileges } = await esClient.security.hasPrivileges({ body: { cluster: ['manage_service_account'] }, }); @@ -59,7 +60,7 @@ export const getCheckPermissionsHandler: FleetRequestHandler< export const generateServiceTokenHandler: RequestHandler = async (context, request, response) => { // Generate the fleet server service token as the current user as the internal user do not have the correct permissions - const esClient = context.core.elasticsearch.client.asCurrentUser; + const esClient = (await context.core).elasticsearch.client.asCurrentUser; try { const tokenResponse = await esClient.transport.request<{ created?: boolean; diff --git a/x-pack/plugins/fleet/server/routes/data_streams/handlers.ts b/x-pack/plugins/fleet/server/routes/data_streams/handlers.ts index d7081a91453f..2d01344a930a 100644 --- a/x-pack/plugins/fleet/server/routes/data_streams/handlers.ts +++ b/x-pack/plugins/fleet/server/routes/data_streams/handlers.ts @@ -39,7 +39,8 @@ interface ESDataStreamInfo { export const getListHandler: RequestHandler = async (context, request, response) => { // Query datastreams as the current user as the Kibana internal user may not have all the required permission - const esClient = context.core.elasticsearch.client.asCurrentUser; + const { savedObjects, elasticsearch } = await context.core; + const esClient = elasticsearch.client.asCurrentUser; const body: GetDataStreamsResponse = { data_streams: [], @@ -54,7 +55,7 @@ export const getListHandler: RequestHandler = async (context, request, response) ] = await Promise.all([ esClient.indices.getDataStream({ name: DATA_STREAM_INDEX_PATTERN }), esClient.indices.dataStreamsStats({ name: DATA_STREAM_INDEX_PATTERN, human: true }), - getPackageSavedObjects(context.core.savedObjects.client), + getPackageSavedObjects(savedObjects.client), ]); const dataStreamsInfoByName = keyBy(dataStreamsInfo, 'name'); @@ -81,7 +82,7 @@ export const getListHandler: RequestHandler = async (context, request, response) allDashboards[pkgSavedObject.id] = dashboards; return allDashboards; }, {}); - const allDashboardSavedObjectsResponse = await context.core.savedObjects.client.bulkGet<{ + const allDashboardSavedObjectsResponse = await savedObjects.client.bulkGet<{ title?: string; }>( Object.values(dashboardIdsByPackageName).flatMap((dashboardIds) => diff --git a/x-pack/plugins/fleet/server/routes/enrollment_api_key/handler.ts b/x-pack/plugins/fleet/server/routes/enrollment_api_key/handler.ts index 1dc088cfe9a3..613bf1ad99be 100644 --- a/x-pack/plugins/fleet/server/routes/enrollment_api_key/handler.ts +++ b/x-pack/plugins/fleet/server/routes/enrollment_api_key/handler.ts @@ -29,7 +29,7 @@ export const getEnrollmentApiKeysHandler: RequestHandler< TypeOf > = async (context, request, response) => { // Use kibana_system and depend on authz checks on HTTP layer to prevent abuse - const esClient = context.core.elasticsearch.client.asInternalUser; + const esClient = (await context.core).elasticsearch.client.asInternalUser; try { const { items, total, page, perPage } = await APIKeyService.listEnrollmentApiKeys(esClient, { @@ -55,8 +55,9 @@ export const postEnrollmentApiKeyHandler: RequestHandler< undefined, TypeOf > = async (context, request, response) => { - const soClient = context.core.savedObjects.client; - const esClient = context.core.elasticsearch.client.asInternalUser; + const { elasticsearch, savedObjects } = await context.core; + const soClient = savedObjects.client; + const esClient = elasticsearch.client.asInternalUser; try { // validate policy id await agentPolicyService.get(soClient, request.body.policy_id).catch((err) => { @@ -84,7 +85,7 @@ export const postEnrollmentApiKeyHandler: RequestHandler< export const deleteEnrollmentApiKeyHandler: RequestHandler< TypeOf > = async (context, request, response) => { - const esClient = context.core.elasticsearch.client.asInternalUser; + const esClient = (await context.core).elasticsearch.client.asInternalUser; try { await APIKeyService.deleteEnrollmentApiKey(esClient, request.params.keyId); @@ -105,7 +106,7 @@ export const getOneEnrollmentApiKeyHandler: RequestHandler< TypeOf > = async (context, request, response) => { // Use kibana_system and depend on authz checks on HTTP layer to prevent abuse - const esClient = context.core.elasticsearch.client.asInternalUser; + const esClient = (await context.core).elasticsearch.client.asInternalUser; try { const apiKey = await APIKeyService.getEnrollmentAPIKey(esClient, request.params.keyId); const body: GetOneEnrollmentAPIKeyResponse = { item: apiKey }; diff --git a/x-pack/plugins/fleet/server/routes/epm/handlers.ts b/x-pack/plugins/fleet/server/routes/epm/handlers.ts index 62a9661c328a..c5ac0a6c8202 100644 --- a/x-pack/plugins/fleet/server/routes/epm/handlers.ts +++ b/x-pack/plugins/fleet/server/routes/epm/handlers.ts @@ -43,7 +43,7 @@ import { getCategories, getPackages, getFile, - getPackageInfoFromRegistry, + getPackageInfo, isBulkInstallError, installPackage, removeInstallation, @@ -83,7 +83,7 @@ export const getListHandler: FleetRequestHandler< TypeOf > = async (context, request, response) => { try { - const savedObjectsClient = context.fleet.epm.internalSoClient; + const savedObjectsClient = (await context.fleet).epm.internalSoClient; const res = await getPackages({ savedObjectsClient, ...request.query, @@ -102,7 +102,7 @@ export const getListHandler: FleetRequestHandler< export const getLimitedListHandler: FleetRequestHandler = async (context, request, response) => { try { - const savedObjectsClient = context.fleet.epm.internalSoClient; + const savedObjectsClient = (await context.fleet).epm.internalSoClient; const res = await getLimitedPackages({ savedObjectsClient }); const body: GetLimitedPackagesResponse = { items: res, @@ -121,7 +121,7 @@ export const getFileHandler: FleetRequestHandler< > = async (context, request, response) => { try { const { pkgName, pkgVersion, filePath } = request.params; - const savedObjectsClient = context.fleet.epm.internalSoClient; + const savedObjectsClient = (await context.fleet).epm.internalSoClient; const installation = await getInstallation({ savedObjectsClient, pkgName }); const useLocalFile = pkgVersion === installation?.version; @@ -194,15 +194,16 @@ export const getInfoHandler: FleetRequestHandler< TypeOf > = async (context, request, response) => { try { - const savedObjectsClient = context.fleet.epm.internalSoClient; + const savedObjectsClient = (await context.fleet).epm.internalSoClient; const { pkgName, pkgVersion } = request.params; if (pkgVersion && !semverValid(pkgVersion)) { throw new IngestManagerError('Package version is not a valid semver'); } - const res = await getPackageInfoFromRegistry({ + const res = await getPackageInfo({ savedObjectsClient, pkgName, pkgVersion: pkgVersion || '', + skipArchive: true, }); const body: GetInfoResponse = { item: res, @@ -219,7 +220,7 @@ export const updatePackageHandler: FleetRequestHandler< TypeOf > = async (context, request, response) => { try { - const savedObjectsClient = context.fleet.epm.internalSoClient; + const savedObjectsClient = (await context.fleet).epm.internalSoClient; const { pkgName } = request.params; const res = await updatePackage({ savedObjectsClient, pkgName, ...request.body }); @@ -238,7 +239,7 @@ export const getStatsHandler: FleetRequestHandler< > = async (context, request, response) => { try { const { pkgName } = request.params; - const savedObjectsClient = context.fleet.epm.internalSoClient; + const savedObjectsClient = (await context.fleet).epm.internalSoClient; const body: GetStatsResponse = { response: await getPackageUsageStats({ savedObjectsClient, pkgName }), }; @@ -253,11 +254,13 @@ export const installPackageFromRegistryHandler: FleetRequestHandler< undefined, TypeOf > = async (context, request, response) => { - const savedObjectsClient = context.fleet.epm.internalSoClient; - const esClient = context.core.elasticsearch.client.asInternalUser; + const coreContext = await context.core; + const fleetContext = await context.fleet; + const savedObjectsClient = fleetContext.epm.internalSoClient; + const esClient = coreContext.elasticsearch.client.asInternalUser; const { pkgName, pkgVersion } = request.params; - const spaceId = context.fleet.spaceId; + const spaceId = fleetContext.spaceId; const res = await installPackage({ installSource: 'registry', savedObjectsClient, @@ -301,9 +304,11 @@ export const bulkInstallPackagesFromRegistryHandler: FleetRequestHandler< undefined, TypeOf > = async (context, request, response) => { - const savedObjectsClient = context.fleet.epm.internalSoClient; - const esClient = context.core.elasticsearch.client.asInternalUser; - const spaceId = context.fleet.spaceId; + const coreContext = await context.core; + const fleetContext = await context.fleet; + const savedObjectsClient = fleetContext.epm.internalSoClient; + const esClient = coreContext.elasticsearch.client.asInternalUser; + const spaceId = fleetContext.spaceId; const bulkInstalledResponses = await bulkInstallPackages({ savedObjectsClient, esClient, @@ -329,11 +334,13 @@ export const installPackageByUploadHandler: FleetRequestHandler< body: { message: 'Requires Enterprise license' }, }); } - const savedObjectsClient = context.fleet.epm.internalSoClient; - const esClient = context.core.elasticsearch.client.asInternalUser; + const coreContext = await context.core; + const fleetContext = await context.fleet; + const savedObjectsClient = fleetContext.epm.internalSoClient; + const esClient = coreContext.elasticsearch.client.asInternalUser; const contentType = request.headers['content-type'] as string; // from types it could also be string[] or undefined but this is checked later const archiveBuffer = Buffer.from(request.body); - const spaceId = context.fleet.spaceId; + const spaceId = fleetContext.spaceId; const res = await installPackage({ installSource: 'upload', savedObjectsClient, @@ -363,8 +370,10 @@ export const deletePackageHandler: FleetRequestHandler< > = async (context, request, response) => { try { const { pkgName, pkgVersion } = request.params; - const savedObjectsClient = context.fleet.epm.internalSoClient; - const esClient = context.core.elasticsearch.client.asInternalUser; + const coreContext = await context.core; + const fleetContext = await context.fleet; + const savedObjectsClient = fleetContext.epm.internalSoClient; + const esClient = coreContext.elasticsearch.client.asInternalUser; const res = await removeInstallation({ savedObjectsClient, pkgName, diff --git a/x-pack/plugins/fleet/server/routes/output/handler.ts b/x-pack/plugins/fleet/server/routes/output/handler.ts index 7f97477d8930..a995a3eeb41d 100644 --- a/x-pack/plugins/fleet/server/routes/output/handler.ts +++ b/x-pack/plugins/fleet/server/routes/output/handler.ts @@ -26,7 +26,7 @@ import { agentPolicyService } from '../../services'; import { generateLogstashApiKey, canCreateLogstashApiKey } from '../../services/api_keys'; export const getOutputsHandler: RequestHandler = async (context, request, response) => { - const soClient = context.core.savedObjects.client; + const soClient = (await context.core).savedObjects.client; try { const outputs = await outputService.list(soClient); @@ -46,7 +46,7 @@ export const getOutputsHandler: RequestHandler = async (context, request, respon export const getOneOuputHandler: RequestHandler< TypeOf > = async (context, request, response) => { - const soClient = context.core.savedObjects.client; + const soClient = (await context.core).savedObjects.client; try { const output = await outputService.get(soClient, request.params.outputId); @@ -71,8 +71,9 @@ export const putOuputHandler: RequestHandler< undefined, TypeOf > = async (context, request, response) => { - const soClient = context.core.savedObjects.client; - const esClient = context.core.elasticsearch.client.asInternalUser; + const coreContext = await context.core; + const soClient = coreContext.savedObjects.client; + const esClient = coreContext.elasticsearch.client.asInternalUser; try { await outputService.update(soClient, request.params.outputId, request.body); const output = await outputService.get(soClient, request.params.outputId); @@ -103,8 +104,9 @@ export const postOuputHandler: RequestHandler< undefined, TypeOf > = async (context, request, response) => { - const soClient = context.core.savedObjects.client; - const esClient = context.core.elasticsearch.client.asInternalUser; + const coreContext = await context.core; + const soClient = coreContext.savedObjects.client; + const esClient = coreContext.elasticsearch.client.asInternalUser; try { const { id, ...data } = request.body; const output = await outputService.create(soClient, data, { id }); @@ -125,7 +127,7 @@ export const postOuputHandler: RequestHandler< export const deleteOutputHandler: RequestHandler< TypeOf > = async (context, request, response) => { - const soClient = context.core.savedObjects.client; + const soClient = (await context.core).savedObjects.client; try { await outputService.delete(soClient, request.params.outputId); @@ -146,7 +148,7 @@ export const deleteOutputHandler: RequestHandler< }; export const postLogstashApiKeyHandler: RequestHandler = async (context, request, response) => { - const esClient = context.core.elasticsearch.client.asCurrentUser; + const esClient = (await context.core).elasticsearch.client.asCurrentUser; try { const hasCreatePrivileges = await canCreateLogstashApiKey(esClient); if (!hasCreatePrivileges) { diff --git a/x-pack/plugins/fleet/server/routes/package_policy/handlers.test.ts b/x-pack/plugins/fleet/server/routes/package_policy/handlers.test.ts index 1041dcc0579a..222408c6e052 100644 --- a/x-pack/plugins/fleet/server/routes/package_policy/handlers.test.ts +++ b/x-pack/plugins/fleet/server/routes/package_policy/handlers.test.ts @@ -85,7 +85,7 @@ jest.mock( jest.mock('../../services/epm/packages', () => { return { ensureInstalledPackage: jest.fn(() => Promise.resolve()), - getPackageInfoFromRegistry: jest.fn(() => Promise.resolve()), + getPackageInfo: jest.fn(() => Promise.resolve()), }; }); @@ -93,7 +93,7 @@ describe('When calling package policy', () => { let routerMock: jest.Mocked; let routeHandler: FleetRequestHandler; let routeConfig: RouteConfig; - let context: jest.Mocked; + let context: FleetRequestHandlerContext; let response: ReturnType; beforeEach(() => { diff --git a/x-pack/plugins/fleet/server/routes/package_policy/handlers.ts b/x-pack/plugins/fleet/server/routes/package_policy/handlers.ts index e0dc77fc57dc..ad46f25ff91d 100644 --- a/x-pack/plugins/fleet/server/routes/package_policy/handlers.ts +++ b/x-pack/plugins/fleet/server/routes/package_policy/handlers.ts @@ -35,7 +35,7 @@ export const getPackagePoliciesHandler: RequestHandler< undefined, TypeOf > = async (context, request, response) => { - const soClient = context.core.savedObjects.client; + const soClient = (await context.core).savedObjects.client; try { const { items, total, page, perPage } = await packagePolicyService.list( soClient, @@ -57,7 +57,7 @@ export const getPackagePoliciesHandler: RequestHandler< export const getOnePackagePolicyHandler: RequestHandler< TypeOf > = async (context, request, response) => { - const soClient = context.core.savedObjects.client; + const soClient = (await context.core).savedObjects.client; const { packagePolicyId } = request.params; const notFoundResponse = () => response.notFound({ body: { message: `Package policy ${packagePolicyId} not found` } }); @@ -87,11 +87,13 @@ export const createPackagePolicyHandler: FleetRequestHandler< undefined, TypeOf > = async (context, request, response) => { - const soClient = context.fleet.epm.internalSoClient; - const esClient = context.core.elasticsearch.client.asInternalUser; + const coreContext = await context.core; + const fleetContext = await context.fleet; + const soClient = fleetContext.epm.internalSoClient; + const esClient = coreContext.elasticsearch.client.asInternalUser; const user = appContextService.getSecurity()?.authc.getCurrentUser(request) || undefined; const { force, ...newPolicy } = request.body; - const spaceId = context.fleet.spaceId; + const spaceId = fleetContext.spaceId; try { const newPackagePolicy = await packagePolicyService.enrichPolicyWithDefaultsFromPackage( soClient, @@ -140,8 +142,9 @@ export const updatePackagePolicyHandler: RequestHandler< unknown, TypeOf > = async (context, request, response) => { - const soClient = context.core.savedObjects.client; - const esClient = context.core.elasticsearch.client.asInternalUser; + const coreContext = await context.core; + const soClient = coreContext.savedObjects.client; + const esClient = coreContext.elasticsearch.client.asInternalUser; const user = appContextService.getSecurity()?.authc.getCurrentUser(request) || undefined; const packagePolicy = await packagePolicyService.get(soClient, request.params.packagePolicyId); @@ -206,8 +209,9 @@ export const deletePackagePolicyHandler: RequestHandler< unknown, TypeOf > = async (context, request, response) => { - const soClient = context.core.savedObjects.client; - const esClient = context.core.elasticsearch.client.asInternalUser; + const coreContext = await context.core; + const soClient = coreContext.savedObjects.client; + const esClient = coreContext.elasticsearch.client.asInternalUser; const user = appContextService.getSecurity()?.authc.getCurrentUser(request) || undefined; try { const body: DeletePackagePoliciesResponse = await packagePolicyService.delete( @@ -241,8 +245,9 @@ export const upgradePackagePolicyHandler: RequestHandler< unknown, TypeOf > = async (context, request, response) => { - const soClient = context.core.savedObjects.client; - const esClient = context.core.elasticsearch.client.asInternalUser; + const coreContext = await context.core; + const soClient = coreContext.savedObjects.client; + const esClient = coreContext.elasticsearch.client.asInternalUser; const user = appContextService.getSecurity()?.authc.getCurrentUser(request) || undefined; try { const body: UpgradePackagePolicyResponse = await packagePolicyService.upgrade( @@ -273,7 +278,7 @@ export const dryRunUpgradePackagePolicyHandler: RequestHandler< unknown, TypeOf > = async (context, request, response) => { - const soClient = context.core.savedObjects.client; + const soClient = (await context.core).savedObjects.client; try { const body: UpgradePackagePolicyDryRunResponse = []; const { packagePolicyIds } = request.body; diff --git a/x-pack/plugins/fleet/server/routes/preconfiguration/handler.ts b/x-pack/plugins/fleet/server/routes/preconfiguration/handler.ts index aa9d6acbbc47..bd0ed690ec6f 100644 --- a/x-pack/plugins/fleet/server/routes/preconfiguration/handler.ts +++ b/x-pack/plugins/fleet/server/routes/preconfiguration/handler.ts @@ -23,10 +23,12 @@ export const updatePreconfigurationHandler: FleetRequestHandler< undefined, TypeOf > = async (context, request, response) => { - const soClient = context.core.savedObjects.client; - const esClient = context.core.elasticsearch.client.asInternalUser; + const coreContext = await context.core; + const fleetContext = await context.fleet; + const soClient = coreContext.savedObjects.client; + const esClient = coreContext.elasticsearch.client.asInternalUser; const defaultOutput = await outputService.ensureDefaultOutput(soClient); - const spaceId = context.fleet.spaceId; + const spaceId = fleetContext.spaceId; const { agentPolicies, packages } = request.body; try { @@ -49,8 +51,9 @@ export const resetOnePreconfigurationHandler: FleetRequestHandler< undefined, undefined > = async (context, request, response) => { - const soClient = context.core.savedObjects.client; - const esClient = context.core.elasticsearch.client.asInternalUser; + const coreContext = await context.core; + const soClient = coreContext.savedObjects.client; + const esClient = coreContext.elasticsearch.client.asInternalUser; try { await resetPreconfiguredAgentPolicies(soClient, esClient, request.params.agentPolicyId); @@ -65,8 +68,9 @@ export const resetPreconfigurationHandler: FleetRequestHandler< undefined, undefined > = async (context, request, response) => { - const soClient = context.core.savedObjects.client; - const esClient = context.core.elasticsearch.client.asInternalUser; + const coreContext = await context.core; + const soClient = coreContext.savedObjects.client; + const esClient = coreContext.elasticsearch.client.asInternalUser; try { await resetPreconfiguredAgentPolicies(soClient, esClient); diff --git a/x-pack/plugins/fleet/server/routes/settings/index.ts b/x-pack/plugins/fleet/server/routes/settings/index.ts index a6203de097c7..07bd3342ade1 100644 --- a/x-pack/plugins/fleet/server/routes/settings/index.ts +++ b/x-pack/plugins/fleet/server/routes/settings/index.ts @@ -15,7 +15,7 @@ import { settingsService, agentPolicyService, appContextService } from '../../se import type { FleetAuthzRouter } from '../security'; export const getSettingsHandler: FleetRequestHandler = async (context, request, response) => { - const soClient = context.fleet.epm.internalSoClient; + const soClient = (await context.fleet).epm.internalSoClient; try { const settings = await settingsService.getSettings(soClient); @@ -39,8 +39,8 @@ export const putSettingsHandler: FleetRequestHandler< undefined, TypeOf > = async (context, request, response) => { - const soClient = context.fleet.epm.internalSoClient; - const esClient = context.core.elasticsearch.client.asInternalUser; + const soClient = (await context.fleet).epm.internalSoClient; + const esClient = (await context.core).elasticsearch.client.asInternalUser; const user = await appContextService.getSecurity()?.authc.getCurrentUser(request); try { diff --git a/x-pack/plugins/fleet/server/routes/setup/handlers.test.ts b/x-pack/plugins/fleet/server/routes/setup/handlers.test.ts index 971684daff65..400c18e85415 100644 --- a/x-pack/plugins/fleet/server/routes/setup/handlers.test.ts +++ b/x-pack/plugins/fleet/server/routes/setup/handlers.test.ts @@ -5,7 +5,8 @@ * 2.0. */ -import { httpServerMock, savedObjectsClientMock } from '@kbn/core/server/mocks'; +import type { AwaitedProperties } from '@kbn/utility-types'; +import { httpServerMock, savedObjectsClientMock, coreMock } from '@kbn/core/server/mocks'; import type { PostFleetSetupResponse } from '../../../common'; import { RegistryError } from '../../errors'; @@ -29,7 +30,7 @@ jest.mock('../../services/setup', () => { const mockSetupFleet = setupFleet as jest.MockedFunction; describe('FleetSetupHandler', () => { - let context: FleetRequestHandlerContext; + let context: AwaitedProperties>; let response: ReturnType; let request: ReturnType; @@ -69,7 +70,7 @@ describe('FleetSetupHandler', () => { nonFatalErrors: [], }) ); - await fleetSetupHandler(context, request, response); + await fleetSetupHandler(coreMock.createCustomRequestHandlerContext(context), request, response); const expectedBody: PostFleetSetupResponse = { isInitialized: true, @@ -81,7 +82,7 @@ describe('FleetSetupHandler', () => { it('POST /setup fails w/500 on custom error', async () => { mockSetupFleet.mockImplementation(() => Promise.reject(new Error('SO method mocked to throw'))); - await fleetSetupHandler(context, request, response); + await fleetSetupHandler(coreMock.createCustomRequestHandlerContext(context), request, response); expect(response.customError).toHaveBeenCalledTimes(1); expect(response.customError).toHaveBeenCalledWith({ @@ -97,7 +98,7 @@ describe('FleetSetupHandler', () => { Promise.reject(new RegistryError('Registry method mocked to throw')) ); - await fleetSetupHandler(context, request, response); + await fleetSetupHandler(coreMock.createCustomRequestHandlerContext(context), request, response); expect(response.customError).toHaveBeenCalledTimes(1); expect(response.customError).toHaveBeenCalledWith({ statusCode: 502, diff --git a/x-pack/plugins/fleet/server/routes/setup/handlers.ts b/x-pack/plugins/fleet/server/routes/setup/handlers.ts index dac5828329a2..59a3516aac83 100644 --- a/x-pack/plugins/fleet/server/routes/setup/handlers.ts +++ b/x-pack/plugins/fleet/server/routes/setup/handlers.ts @@ -17,8 +17,9 @@ export const getFleetStatusHandler: FleetRequestHandler = async (context, reques const isApiKeysEnabled = await appContextService .getSecurity() .authc.apiKeys.areAPIKeysEnabled(); + const coreContext = await context.core; const isFleetServerSetup = await hasFleetServers( - context.core.elasticsearch.client.asInternalUser + coreContext.elasticsearch.client.asInternalUser ); const missingRequirements: GetFleetStatusResponse['missing_requirements'] = []; @@ -52,8 +53,8 @@ export const getFleetStatusHandler: FleetRequestHandler = async (context, reques export const fleetSetupHandler: FleetRequestHandler = async (context, request, response) => { try { - const soClient = context.fleet.epm.internalSoClient; - const esClient = context.core.elasticsearch.client.asInternalUser; + const soClient = (await context.fleet).epm.internalSoClient; + const esClient = (await context.core).elasticsearch.client.asInternalUser; const setupStatus = await setupFleet(soClient, esClient); const body: PostFleetSetupResponse = { ...setupStatus, diff --git a/x-pack/plugins/fleet/server/services/epm/packages/get.test.ts b/x-pack/plugins/fleet/server/services/epm/packages/get.test.ts index 040bb79b9fc7..6c426c4efdf4 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/get.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/get.test.ts @@ -187,6 +187,7 @@ describe('When using EPM `get` services', () => { beforeEach(() => { const mockContract = createAppContextStartContractMock(); appContextService.start(mockContract); + jest.clearAllMocks(); MockRegistry.fetchFindLatestPackageOrUndefined.mockResolvedValue({ name: 'my-package', version: '1.0.0', @@ -321,6 +322,56 @@ describe('When using EPM `get` services', () => { status: 'installed', }); }); + + it('sets the latestVersion to installed version when an installed package is newer than package in registry', async () => { + const soClient = savedObjectsClientMock.create(); + soClient.get.mockResolvedValue({ + id: 'my-package', + type: PACKAGES_SAVED_OBJECT_TYPE, + references: [], + attributes: { + version: '2.0.0', + install_status: 'installed', + }, + }); + + await expect( + getPackageInfo({ + savedObjectsClient: soClient, + pkgName: 'my-package', + pkgVersion: '1.0.0', + }) + ).resolves.toMatchObject({ + latestVersion: '1.0.0', + status: 'installed', + }); + }); + }); + + describe('skipArchive', () => { + it('avoids loading archive when skipArchive = true', async () => { + const soClient = savedObjectsClientMock.create(); + soClient.get.mockRejectedValue(SavedObjectsErrorHelpers.createGenericNotFoundError()); + MockRegistry.fetchInfo.mockResolvedValue({ + name: 'my-package', + version: '1.0.0', + assets: [], + } as unknown as RegistryPackage); + + await expect( + getPackageInfo({ + savedObjectsClient: soClient, + pkgName: 'my-package', + pkgVersion: '1.0.0', + skipArchive: true, + }) + ).resolves.toMatchObject({ + latestVersion: '1.0.0', + status: 'not_installed', + }); + + expect(MockRegistry.getRegistryPackage).not.toHaveBeenCalled(); + }); }); }); }); diff --git a/x-pack/plugins/fleet/server/services/epm/packages/get.ts b/x-pack/plugins/fleet/server/services/epm/packages/get.ts index de4ce6a1e84b..0ba42585c160 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/get.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/get.ts @@ -6,6 +6,7 @@ */ import type { SavedObjectsClientContract, SavedObjectsFindOptions } from '@kbn/core/server'; +import semverGte from 'semver/functions/gte'; import { isPackageLimited, @@ -98,52 +99,18 @@ export async function getPackageSavedObjects( export const getInstallations = getPackageSavedObjects; -export async function getPackageInfoFromRegistry(options: { - savedObjectsClient: SavedObjectsClientContract; - pkgName: string; - pkgVersion: string; -}): Promise { - const { savedObjectsClient, pkgName, pkgVersion } = options; - const [savedObject, latestPackage] = await Promise.all([ - getInstallationObject({ savedObjectsClient, pkgName }), - Registry.fetchFindLatestPackageOrThrow(pkgName), - ]); - - // If no package version is provided, use the installed version in the response - let responsePkgVersion = pkgVersion || savedObject?.attributes.install_version; - // If no installed version of the given package exists, default to the latest version of the package - if (!responsePkgVersion) { - responsePkgVersion = latestPackage.version; - } - const packageInfo = await Registry.fetchInfo(pkgName, responsePkgVersion); - - // Fix the paths - const paths = - packageInfo?.assets?.map((path) => - path.replace(`/package/${pkgName}/${pkgVersion}`, `${pkgName}-${pkgVersion}`) - ) ?? []; - - // add properties that aren't (or aren't yet) on the package - const additions: EpmPackageAdditions = { - latestVersion: latestPackage.version, - title: packageInfo.title || nameAsTitle(packageInfo.name), - assets: Registry.groupPathsByService(paths || []), - removable: true, - notice: Registry.getNoticePath(paths || []), - keepPoliciesUpToDate: savedObject?.attributes.keep_policies_up_to_date ?? false, - }; - const updated = { ...packageInfo, ...additions }; - - return createInstallableFrom(updated, savedObject); -} - -export async function getPackageInfo(options: { +export async function getPackageInfo({ + savedObjectsClient, + pkgName, + pkgVersion, + skipArchive = false, +}: { savedObjectsClient: SavedObjectsClientContract; pkgName: string; pkgVersion: string; + /** Avoid loading the registry archive into the cache (only use for performance reasons). Defaults to `false` */ + skipArchive?: boolean; }): Promise { - const { savedObjectsClient, pkgName, pkgVersion } = options; - const [savedObject, latestPackage] = await Promise.all([ getInstallationObject({ savedObjectsClient, pkgName }), Registry.fetchFindLatestPackageOrUndefined(pkgName), @@ -154,20 +121,39 @@ export async function getPackageInfo(options: { } // If no package version is provided, use the installed version in the response, fallback to package from registry - const responsePkgVersion = - pkgVersion ?? savedObject?.attributes.install_version ?? latestPackage!.version; - - const getPackageRes = await getPackageFromSource({ - pkgName, - pkgVersion: responsePkgVersion, - savedObjectsClient, - installedPkg: savedObject?.attributes, - }); - const { paths, packageInfo } = getPackageRes; + const resolvedPkgVersion = + pkgVersion !== '' + ? pkgVersion + : savedObject?.attributes.install_version ?? latestPackage!.version; + + // If same version is available in registry and skipArchive is true, use the info from the registry (faster), + // otherwise build it from the archive + let paths: string[]; + let packageInfo: RegistryPackage | ArchivePackage | undefined = skipArchive + ? await Registry.fetchInfo(pkgName, pkgVersion).catch(() => undefined) + : undefined; + + if (packageInfo) { + // Fix the paths + paths = + packageInfo.assets?.map((path) => + path.replace(`/package/${pkgName}/${pkgVersion}`, `${pkgName}-${pkgVersion}`) + ) ?? []; + } else { + ({ paths, packageInfo } = await getPackageFromSource({ + pkgName, + pkgVersion: resolvedPkgVersion, + savedObjectsClient, + installedPkg: savedObject?.attributes, + })); + } // add properties that aren't (or aren't yet) on the package const additions: EpmPackageAdditions = { - latestVersion: latestPackage?.version ?? responsePkgVersion, + latestVersion: + latestPackage?.version && semverGte(latestPackage.version, resolvedPkgVersion) + ? latestPackage.version + : resolvedPkgVersion, title: packageInfo.title || nameAsTitle(packageInfo.name), assets: Registry.groupPathsByService(paths || []), removable: true, diff --git a/x-pack/plugins/fleet/server/services/epm/packages/index.ts b/x-pack/plugins/fleet/server/services/epm/packages/index.ts index 30d2b5ec3fd2..bfb09abcfaa2 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/index.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/index.ts @@ -20,7 +20,6 @@ export { getInstallation, getInstallations, getPackageInfo, - getPackageInfoFromRegistry, getPackages, getLimitedPackages, } from './get'; diff --git a/x-pack/plugins/fleet/server/services/package_policy.test.ts b/x-pack/plugins/fleet/server/services/package_policy.test.ts index 53218e7cda8f..c84701bf5d5e 100644 --- a/x-pack/plugins/fleet/server/services/package_policy.test.ts +++ b/x-pack/plugins/fleet/server/services/package_policy.test.ts @@ -9,6 +9,7 @@ import { elasticsearchServiceMock, savedObjectsClientMock, httpServerMock, + coreMock, } from '@kbn/core/server/mocks'; import { produce } from 'immer'; import type { @@ -1270,7 +1271,7 @@ describe('Package policy service', () => { await packagePolicyService.runExternalCallbacks( 'packagePolicyCreate', newPackagePolicy, - context, + coreMock.createCustomRequestHandlerContext(context), request ); expect(callbackCallingOrder).toEqual(['a', 'b']); @@ -1283,7 +1284,7 @@ describe('Package policy service', () => { await packagePolicyService.runExternalCallbacks( 'packagePolicyCreate', newPackagePolicy, - context, + coreMock.createCustomRequestHandlerContext(context), request ); @@ -1330,7 +1331,7 @@ describe('Package policy service', () => { await packagePolicyService.runExternalCallbacks( 'packagePolicyCreate', newPackagePolicy, - context, + coreMock.createCustomRequestHandlerContext(context), request ); } catch (e) { @@ -1349,7 +1350,7 @@ describe('Package policy service', () => { packagePolicyService.runExternalCallbacks( 'packagePolicyCreate', newPackagePolicy, - context, + coreMock.createCustomRequestHandlerContext(context), request ) ).rejects.toThrow('callbackThree threw error on purpose'); @@ -1438,15 +1439,16 @@ describe('Package policy service', () => { appContextService.addExternalCallback('packagePolicyPostCreate', callbackA); appContextService.addExternalCallback('packagePolicyPostCreate', callbackB); + const requestContext = coreMock.createCustomRequestHandlerContext(context); await packagePolicyService.runExternalCallbacks( 'packagePolicyPostCreate', packagePolicy, - context, + requestContext, request ); - expect(callbackA).toHaveBeenCalledWith(packagePolicy, context, request); - expect(callbackB).toHaveBeenCalledWith(packagePolicy, context, request); + expect(callbackA).toHaveBeenCalledWith(packagePolicy, requestContext, request); + expect(callbackB).toHaveBeenCalledWith(packagePolicy, requestContext, request); expect(callbackCallingOrder).toEqual(['a', 'b']); }); }); @@ -3326,6 +3328,7 @@ describe('Package policy service', () => { beforeEach(() => { savedObjectsClient = savedObjectsClientMock.create(); }); + function mockPackage(pkgName: string) { const mockPackagePolicy = createPackagePolicyMock(); @@ -3346,6 +3349,7 @@ describe('Package policy service', () => { attributes, }); } + it('should return success if package and policy versions match', async () => { mockPackage('apache'); diff --git a/x-pack/plugins/fleet/server/types/request_context.ts b/x-pack/plugins/fleet/server/types/request_context.ts index 0621319ba011..f9972c626234 100644 --- a/x-pack/plugins/fleet/server/types/request_context.ts +++ b/x-pack/plugins/fleet/server/types/request_context.ts @@ -8,7 +8,7 @@ import type { KibanaResponseFactory, RequestHandler, - RequestHandlerContext, + CustomRequestHandlerContext, RouteMethod, SavedObjectsClientContract, IRouter, @@ -18,7 +18,7 @@ import type { FleetAuthz } from '../../common/authz'; import type { AgentClient } from '../services'; /** @internal */ -export interface FleetRequestHandlerContext extends RequestHandlerContext { +export type FleetRequestHandlerContext = CustomRequestHandlerContext<{ fleet: { /** {@link FleetAuthz} */ authz: FleetAuthz; @@ -36,7 +36,7 @@ export interface FleetRequestHandlerContext extends RequestHandlerContext { }; spaceId: string; }; -} +}>; /** * Convenience type for request handlers in Fleet that includes the FleetRequestHandlerContext type diff --git a/x-pack/plugins/global_search/server/mocks.ts b/x-pack/plugins/global_search/server/mocks.ts index b27c9f7a9e6b..948ea49b95fa 100644 --- a/x-pack/plugins/global_search/server/mocks.ts +++ b/x-pack/plugins/global_search/server/mocks.ts @@ -52,7 +52,10 @@ const createRequestHandlerContextMock = (): jest.Mocked { async (ctx, req, res) => { const { params, options } = req.body; try { - const allResults = await ctx - .globalSearch!.find(params, { ...options, aborted$: req.events.aborted$ }) + const globalSearch = await ctx.globalSearch; + const allResults = await globalSearch + .find(params, { ...options, aborted$: req.events.aborted$ }) .pipe( map((batch) => batch.results), reduce((acc, results) => [...acc, ...results]) diff --git a/x-pack/plugins/global_search/server/routes/get_searchable_types.ts b/x-pack/plugins/global_search/server/routes/get_searchable_types.ts index 3db7a4ee7517..f456c0e665f1 100644 --- a/x-pack/plugins/global_search/server/routes/get_searchable_types.ts +++ b/x-pack/plugins/global_search/server/routes/get_searchable_types.ts @@ -14,7 +14,8 @@ export const registerInternalSearchableTypesRoute = (router: GlobalSearchRouter) validate: false, }, async (ctx, req, res) => { - const types = await ctx.globalSearch!.getSearchableTypes(); + const globalSearch = await ctx.globalSearch; + const types = await globalSearch.getSearchableTypes(); return res.ok({ body: { types, diff --git a/x-pack/plugins/global_search/server/types.ts b/x-pack/plugins/global_search/server/types.ts index 5f20f4f181db..10a7bafe850d 100644 --- a/x-pack/plugins/global_search/server/types.ts +++ b/x-pack/plugins/global_search/server/types.ts @@ -12,7 +12,7 @@ import type { SavedObjectsClientContract, Capabilities, IRouter, - RequestHandlerContext, + CustomRequestHandlerContext, } from '@kbn/core/server'; import { GlobalSearchBatchedResults, @@ -29,9 +29,9 @@ export type GlobalSearchPluginStart = Pick; /** * @internal diff --git a/x-pack/plugins/graph/public/components/search_bar.test.tsx b/x-pack/plugins/graph/public/components/search_bar.test.tsx index 3589c6c25b29..c05da957599c 100644 --- a/x-pack/plugins/graph/public/components/search_bar.test.tsx +++ b/x-pack/plugins/graph/public/components/search_bar.test.tsx @@ -19,7 +19,8 @@ import { import { act } from 'react-dom/test-utils'; import { QueryStringInput } from '@kbn/unified-search-plugin/public'; import type { DataView } from '@kbn/data-views-plugin/public'; - +import { setAutocomplete } from '@kbn/unified-search-plugin/public/services'; +import { unifiedSearchPluginMock } from '@kbn/unified-search-plugin/public/mocks'; import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; import { I18nProvider, InjectedIntl } from '@kbn/i18n-react'; @@ -59,6 +60,8 @@ function getServiceMocks() { query: { savedQueries: {}, }, + }, + unifiedSearch: { autocomplete: { hasQuerySuggestions: () => false, }, @@ -101,6 +104,11 @@ describe('search_bar', () => { }, }; + beforeEach(() => { + const autocompleteStart = unifiedSearchPluginMock.createStartContract(); + setAutocomplete(autocompleteStart.autocomplete); + }); + beforeEach(() => { store = createMockGraphStore({ sagas: [submitSearchSaga], diff --git a/x-pack/plugins/graph/server/routes/explore.ts b/x-pack/plugins/graph/server/routes/explore.ts index b3dddfb21cb9..6a6072e17274 100644 --- a/x-pack/plugins/graph/server/routes/explore.ts +++ b/x-pack/plugins/graph/server/routes/explore.ts @@ -34,50 +34,44 @@ export function registerExploreRoute({ }), }, }, - router.handleLegacyErrors( - async ( - { - core: { - elasticsearch: { client: esClient }, + router.handleLegacyErrors(async ({ core }, request, response) => { + verifyApiAccess(licenseState); + licenseState.notifyUsage('Graph'); + + const { + elasticsearch: { client: esClient }, + } = await core; + try { + return response.ok({ + body: { + resp: await esClient.asCurrentUser.transport.request({ + path: '/' + encodeURIComponent(request.body.index) + '/_graph/explore', + body: request.body.query, + method: 'POST', + }), }, - }, - request, - response - ) => { - verifyApiAccess(licenseState); - licenseState.notifyUsage('Graph'); - try { - return response.ok({ - body: { - resp: await esClient.asCurrentUser.transport.request({ - path: '/' + encodeURIComponent(request.body.index) + '/_graph/explore', - body: request.body.query, - method: 'POST', - }), - }, + }); + } catch (error) { + if (error instanceof errors.ResponseError) { + const errorBody: ErrorResponse = error.body; + const relevantCause = (errorBody.error?.root_cause ?? []).find((cause) => { + return ( + cause.reason.includes('Fielddata is disabled on text fields') || + cause.reason.includes('No support for examining floating point') || + cause.reason.includes('Sample diversifying key must be a single valued-field') || + cause.reason.includes('Failed to parse query') || + cause.reason.includes('Text fields are not optimised for operations') || + cause.type === 'parsing_exception' + ); }); - } catch (error) { - if (error instanceof errors.ResponseError) { - const errorBody: ErrorResponse = error.body; - const relevantCause = (errorBody.error?.root_cause ?? []).find((cause) => { - return ( - cause.reason.includes('Fielddata is disabled on text fields') || - cause.reason.includes('No support for examining floating point') || - cause.reason.includes('Sample diversifying key must be a single valued-field') || - cause.reason.includes('Failed to parse query') || - cause.reason.includes('Text fields are not optimised for operations') || - cause.type === 'parsing_exception' - ); - }); - if (relevantCause) { - throw Boom.badRequest(relevantCause.reason); - } + if (relevantCause) { + throw Boom.badRequest(relevantCause.reason); } - - throw error; } + + throw error; } - ) + }) ); } diff --git a/x-pack/plugins/graph/server/routes/search.ts b/x-pack/plugins/graph/server/routes/search.ts index 8d5827fe1331..2bdb956196ed 100644 --- a/x-pack/plugins/graph/server/routes/search.ts +++ b/x-pack/plugins/graph/server/routes/search.ts @@ -27,40 +27,33 @@ export function registerSearchRoute({ }), }, }, - router.handleLegacyErrors( - async ( - { - core: { - uiSettings: { client: uiSettings }, - elasticsearch: { client: esClient }, + router.handleLegacyErrors(async ({ core }, request, response) => { + verifyApiAccess(licenseState); + licenseState.notifyUsage('Graph'); + const { + uiSettings: { client: uiSettings }, + elasticsearch: { client: esClient }, + } = await core; + const includeFrozen = await uiSettings.get(UI_SETTINGS.SEARCH_INCLUDE_FROZEN); + try { + return response.ok({ + body: { + resp: await esClient.asCurrentUser.search({ + index: request.body.index, + body: request.body.body, + track_total_hits: true, + ...(includeFrozen ? { ignore_throttled: false } : {}), + }), }, - }, - request, - response - ) => { - verifyApiAccess(licenseState); - licenseState.notifyUsage('Graph'); - const includeFrozen = await uiSettings.get(UI_SETTINGS.SEARCH_INCLUDE_FROZEN); - try { - return response.ok({ - body: { - resp: await esClient.asCurrentUser.search({ - index: request.body.index, - body: request.body.body, - track_total_hits: true, - ...(includeFrozen ? { ignore_throttled: false } : {}), - }), - }, - }); - } catch (error) { - return response.customError({ - statusCode: error.statusCode || 500, - body: { - message: error.message, - }, - }); - } + }); + } catch (error) { + return response.customError({ + statusCode: error.statusCode || 500, + body: { + message: error.message, + }, + }); } - ) + }) ); } diff --git a/x-pack/plugins/grokdebugger/server/routes/api/grokdebugger/register_grok_simulate_route.ts b/x-pack/plugins/grokdebugger/server/routes/api/grokdebugger/register_grok_simulate_route.ts index fb20812f3307..3154a1880f4a 100644 --- a/x-pack/plugins/grokdebugger/server/routes/api/grokdebugger/register_grok_simulate_route.ts +++ b/x-pack/plugins/grokdebugger/server/routes/api/grokdebugger/register_grok_simulate_route.ts @@ -34,11 +34,11 @@ export function registerGrokSimulateRoute(framework: KibanaFramework) { }, async (requestContext, request, response) => { try { + const esClient = (await requestContext.core).elasticsearch.client; const grokdebuggerRequest = GrokdebuggerRequest.fromDownstreamJSON(request.body); - const simulateResponseFromES = - await requestContext.core.elasticsearch.client.asCurrentUser.ingest.simulate({ - body: grokdebuggerRequest.upstreamJSON, - }); + const simulateResponseFromES = await esClient.asCurrentUser.ingest.simulate({ + body: grokdebuggerRequest.upstreamJSON, + }); const grokdebuggerResponse = GrokdebuggerResponse.fromUpstreamJSON(simulateResponseFromES); return response.ok({ body: grokdebuggerResponse, diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/index/register_add_policy_route.ts b/x-pack/plugins/index_lifecycle_management/server/routes/api/index/register_add_policy_route.ts index da4668ab3ead..1d4821e10211 100644 --- a/x-pack/plugins/index_lifecycle_management/server/routes/api/index/register_add_policy_route.ts +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/index/register_add_policy_route.ts @@ -45,12 +45,8 @@ export function registerAddPolicyRoute({ const { indexName, policyName, alias = '' } = body; try { - await addLifecyclePolicy( - context.core.elasticsearch.client.asCurrentUser, - indexName, - policyName, - alias - ); + const esClient = (await context.core).elasticsearch.client; + await addLifecyclePolicy(esClient.asCurrentUser, indexName, policyName, alias); return response.ok(); } catch (error) { return handleEsError({ error, response }); diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/index/register_remove_route.ts b/x-pack/plugins/index_lifecycle_management/server/routes/api/index/register_remove_route.ts index 08ea60cb806a..cec5da7aad90 100644 --- a/x-pack/plugins/index_lifecycle_management/server/routes/api/index/register_remove_route.ts +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/index/register_remove_route.ts @@ -39,7 +39,8 @@ export function registerRemoveRoute({ const { indexNames } = body; try { - await removeLifecycle(context.core.elasticsearch.client.asCurrentUser, indexNames); + const esClient = (await context.core).elasticsearch.client; + await removeLifecycle(esClient.asCurrentUser, indexNames); return response.ok(); } catch (error) { return handleEsError({ error, response }); diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/index/register_retry_route.ts b/x-pack/plugins/index_lifecycle_management/server/routes/api/index/register_retry_route.ts index d229e82504c9..42bcffcbd812 100644 --- a/x-pack/plugins/index_lifecycle_management/server/routes/api/index/register_retry_route.ts +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/index/register_retry_route.ts @@ -36,7 +36,8 @@ export function registerRetryRoute({ router, license, lib: { handleEsError } }: const { indexNames } = body; try { - await retryLifecycle(context.core.elasticsearch.client.asCurrentUser, indexNames); + const esClient = (await context.core).elasticsearch.client; + await retryLifecycle(esClient.asCurrentUser, indexNames); return response.ok(); } catch (error) { return handleEsError({ error, response }); diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_details_route.ts b/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_details_route.ts index 3cf26ca96595..13393182d052 100644 --- a/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_details_route.ts +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_details_route.ts @@ -42,7 +42,8 @@ export function registerDetailsRoute({ const { nodeAttrs } = params; try { - const statsResponse = await context.core.elasticsearch.client.asCurrentUser.nodes.stats(); + const esClient = (await context.core).elasticsearch.client; + const statsResponse = await esClient.asCurrentUser.nodes.stats(); const okResponse = { body: findMatchingNodes(statsResponse, nodeAttrs) }; return response.ok(okResponse); } catch (error) { diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_list_route.ts b/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_list_route.ts index cf892ddff67b..a9b17c25110b 100644 --- a/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_list_route.ts +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_list_route.ts @@ -88,14 +88,14 @@ export function registerListRoute({ { path: addBasePath('/nodes/list'), validate: false }, license.guardApiRoute(async (context, request, response) => { try { - const settingsResponse = - await context.core.elasticsearch.client.asCurrentUser.transport.request({ - method: 'GET', - path: '/_nodes/settings', - querystring: { - format: 'json', - }, - }); + const esClient = (await context.core).elasticsearch.client; + const settingsResponse = await esClient.asCurrentUser.transport.request({ + method: 'GET', + path: '/_nodes/settings', + querystring: { + format: 'json', + }, + }); const body: ListNodesRouteResponse = convertSettingsIntoLists( settingsResponse as Settings, disallowedNodeAttributes diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_create_route.ts b/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_create_route.ts index 909f6ca7bf5a..65c37120e552 100644 --- a/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_create_route.ts +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_create_route.ts @@ -55,7 +55,8 @@ export function registerCreateRoute({ const { name, ...rest } = body; try { - await createPolicy(context.core.elasticsearch.client.asCurrentUser, name, rest); + const esClient = (await context.core).elasticsearch.client; + await createPolicy(esClient.asCurrentUser, name, rest); return response.ok(); } catch (error) { return handleEsError({ error, response }); diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_delete_route.ts b/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_delete_route.ts index 9927f093aef2..d113e646f0b3 100644 --- a/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_delete_route.ts +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_delete_route.ts @@ -36,7 +36,8 @@ export function registerDeleteRoute({ const { policyNames } = params; try { - await deletePolicies(context.core.elasticsearch.client.asCurrentUser, policyNames); + const esClient = (await context.core).elasticsearch.client; + await deletePolicies(esClient.asCurrentUser, policyNames); return response.ok(); } catch (error) { return handleEsError({ error, response }); diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_fetch_route.ts b/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_fetch_route.ts index 09f28679d58b..9526aca51d33 100644 --- a/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_fetch_route.ts +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/policies/register_fetch_route.ts @@ -62,7 +62,7 @@ export function registerFetchRoute({ router, license, lib: { handleEsError } }: router.get( { path: addBasePath('/policies'), validate: false }, license.guardApiRoute(async (context, request, response) => { - const { asCurrentUser } = context.core.elasticsearch.client; + const { asCurrentUser } = (await context.core).elasticsearch.client; try { const policiesResponse = await fetchPolicies(asCurrentUser); diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/snapshot_policies/register_fetch_route.ts b/x-pack/plugins/index_lifecycle_management/server/routes/api/snapshot_policies/register_fetch_route.ts index ebe8a2388a02..5a0190828789 100644 --- a/x-pack/plugins/index_lifecycle_management/server/routes/api/snapshot_policies/register_fetch_route.ts +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/snapshot_policies/register_fetch_route.ts @@ -13,8 +13,8 @@ export function registerFetchRoute({ router, license, lib: { handleEsError } }: { path: addBasePath('/snapshot_policies'), validate: false }, license.guardApiRoute(async (context, request, response) => { try { - const policiesByName = - await context.core.elasticsearch.client.asCurrentUser.slm.getLifecycle(); + const esClient = (await context.core).elasticsearch.client; + const policiesByName = await esClient.asCurrentUser.slm.getLifecycle(); return response.ok({ body: Object.keys(policiesByName) }); } catch (error) { return handleEsError({ error, response }); diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/snapshot_repositories/register_fetch_route.ts b/x-pack/plugins/index_lifecycle_management/server/routes/api/snapshot_repositories/register_fetch_route.ts index a4bdbd5fc7af..45a55cfce5da 100644 --- a/x-pack/plugins/index_lifecycle_management/server/routes/api/snapshot_repositories/register_fetch_route.ts +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/snapshot_repositories/register_fetch_route.ts @@ -28,7 +28,8 @@ export const registerFetchRoute = ({ router, license }: RouteDependencies) => { } try { - const esResult = await ctx.core.elasticsearch.client.asCurrentUser.snapshot.getRepository({ + const esClient = (await ctx.core).elasticsearch.client; + const esResult = await esClient.asCurrentUser.snapshot.getRepository({ name: '*', }); const repos: ListSnapshotReposResponse = { diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/templates/register_add_policy_route.ts b/x-pack/plugins/index_lifecycle_management/server/routes/api/templates/register_add_policy_route.ts index 8b8b50834c69..3f6b4ce843e8 100644 --- a/x-pack/plugins/index_lifecycle_management/server/routes/api/templates/register_add_policy_route.ts +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/templates/register_add_policy_route.ts @@ -106,8 +106,9 @@ export function registerAddPolicyRoute({ const { templateName, policyName, aliasName } = body; const isLegacy = (request.query as TypeOf).legacy === 'true'; try { + const esClient = (await context.core).elasticsearch.client; const updatedTemplate = await updateIndexTemplate( - context.core.elasticsearch.client.asCurrentUser, + esClient.asCurrentUser, isLegacy, templateName, policyName, diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/templates/register_fetch_route.ts b/x-pack/plugins/index_lifecycle_management/server/routes/api/templates/register_fetch_route.ts index dfa3f0c6b64c..e70f2ed2bdac 100644 --- a/x-pack/plugins/index_lifecycle_management/server/routes/api/templates/register_fetch_route.ts +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/templates/register_fetch_route.ts @@ -88,10 +88,8 @@ export function registerFetchRoute({ router, license, lib: { handleEsError } }: license.guardApiRoute(async (context, request, response) => { const isLegacy = (request.query as TypeOf).legacy === 'true'; try { - const templates = await fetchTemplates( - context.core.elasticsearch.client.asCurrentUser, - isLegacy - ); + const esClient = (await context.core).elasticsearch.client; + const templates = await fetchTemplates(esClient.asCurrentUser, isLegacy); const okResponse = { body: filterTemplates(templates, isLegacy) }; return response.ok(okResponse); } catch (error) { diff --git a/x-pack/plugins/index_management/server/routes/api/component_templates/register_create_route.ts b/x-pack/plugins/index_management/server/routes/api/component_templates/register_create_route.ts index cc98035f37c5..adfa0d61859f 100644 --- a/x-pack/plugins/index_management/server/routes/api/component_templates/register_create_route.ts +++ b/x-pack/plugins/index_management/server/routes/api/component_templates/register_create_route.ts @@ -24,7 +24,7 @@ export const registerCreateRoute = ({ }, }, async (context, request, response) => { - const { client } = context.core.elasticsearch; + const { client } = (await context.core).elasticsearch; const serializedComponentTemplate = serializeComponentTemplate(request.body); diff --git a/x-pack/plugins/index_management/server/routes/api/component_templates/register_delete_route.ts b/x-pack/plugins/index_management/server/routes/api/component_templates/register_delete_route.ts index 34a644d60716..c06f90821b33 100644 --- a/x-pack/plugins/index_management/server/routes/api/component_templates/register_delete_route.ts +++ b/x-pack/plugins/index_management/server/routes/api/component_templates/register_delete_route.ts @@ -26,7 +26,7 @@ export const registerDeleteRoute = ({ }, }, async (context, request, response) => { - const { client } = context.core.elasticsearch; + const { client } = (await context.core).elasticsearch; const { names } = request.params; const componentNames = names.split(','); diff --git a/x-pack/plugins/index_management/server/routes/api/component_templates/register_get_route.ts b/x-pack/plugins/index_management/server/routes/api/component_templates/register_get_route.ts index caca3c8ce853..73224c0356ad 100644 --- a/x-pack/plugins/index_management/server/routes/api/component_templates/register_get_route.ts +++ b/x-pack/plugins/index_management/server/routes/api/component_templates/register_get_route.ts @@ -24,7 +24,7 @@ export function registerGetAllRoute({ router, lib: { handleEsError } }: RouteDep router.get( { path: addBasePath('/component_templates'), validate: false }, async (context, request, response) => { - const { client } = context.core.elasticsearch; + const { client } = (await context.core).elasticsearch; try { const { component_templates: componentTemplates } = @@ -58,7 +58,7 @@ export function registerGetAllRoute({ router, lib: { handleEsError } }: RouteDep }, }, async (context, request, response) => { - const { client } = context.core.elasticsearch; + const { client } = (await context.core).elasticsearch; const { name } = request.params; try { diff --git a/x-pack/plugins/index_management/server/routes/api/component_templates/register_privileges_route.ts b/x-pack/plugins/index_management/server/routes/api/component_templates/register_privileges_route.ts index d2fe85d219ef..a8b488fb54c9 100644 --- a/x-pack/plugins/index_management/server/routes/api/component_templates/register_privileges_route.ts +++ b/x-pack/plugins/index_management/server/routes/api/component_templates/register_privileges_route.ts @@ -41,7 +41,7 @@ export const registerPrivilegesRoute = ({ return response.ok({ body: privilegesResult }); } - const { client } = context.core.elasticsearch; + const { client } = (await context.core).elasticsearch; try { const { has_all_requested: hasAllPrivileges, cluster } = diff --git a/x-pack/plugins/index_management/server/routes/api/component_templates/register_update_route.ts b/x-pack/plugins/index_management/server/routes/api/component_templates/register_update_route.ts index 016e379165d4..1a9e642cd934 100644 --- a/x-pack/plugins/index_management/server/routes/api/component_templates/register_update_route.ts +++ b/x-pack/plugins/index_management/server/routes/api/component_templates/register_update_route.ts @@ -29,7 +29,7 @@ export const registerUpdateRoute = ({ }, }, async (context, request, response) => { - const { client } = context.core.elasticsearch; + const { client } = (await context.core).elasticsearch; const { name } = request.params; const { template, version, _meta } = request.body; diff --git a/x-pack/plugins/index_management/server/routes/api/data_streams/register_delete_route.ts b/x-pack/plugins/index_management/server/routes/api/data_streams/register_delete_route.ts index 0602aaaaafc6..dcdb4056cb68 100644 --- a/x-pack/plugins/index_management/server/routes/api/data_streams/register_delete_route.ts +++ b/x-pack/plugins/index_management/server/routes/api/data_streams/register_delete_route.ts @@ -21,7 +21,7 @@ export function registerDeleteRoute({ router, lib: { handleEsError } }: RouteDep validate: { body: bodySchema }, }, async (context, request, response) => { - const { client } = context.core.elasticsearch; + const { client } = (await context.core).elasticsearch; const { dataStreams } = request.body as TypeOf; const responseBody: { dataStreamsDeleted: string[]; errors: any[] } = { diff --git a/x-pack/plugins/index_management/server/routes/api/data_streams/register_get_route.ts b/x-pack/plugins/index_management/server/routes/api/data_streams/register_get_route.ts index a698be6f913d..a562cc5f4cc5 100644 --- a/x-pack/plugins/index_management/server/routes/api/data_streams/register_get_route.ts +++ b/x-pack/plugins/index_management/server/routes/api/data_streams/register_get_route.ts @@ -103,7 +103,7 @@ export function registerGetAllRoute({ router, lib: { handleEsError }, config }: router.get( { path: addBasePath('/data_streams'), validate: { query: querySchema } }, async (context, request, response) => { - const { client } = context.core.elasticsearch; + const { client } = (await context.core).elasticsearch; const includeStats = (request.query as TypeOf).includeStats === 'true'; @@ -152,7 +152,7 @@ export function registerGetOneRoute({ router, lib: { handleEsError }, config }: }, async (context, request, response) => { const { name } = request.params as TypeOf; - const { client } = context.core.elasticsearch; + const { client } = (await context.core).elasticsearch; try { const [{ data_streams: dataStreams }, { data_streams: dataStreamsStats }] = await Promise.all([getDataStreams(client, name), getDataStreamsStats(client, name)]); diff --git a/x-pack/plugins/index_management/server/routes/api/indices/register_clear_cache_route.ts b/x-pack/plugins/index_management/server/routes/api/indices/register_clear_cache_route.ts index 9e7c4a82bca5..a46a23b8fe47 100644 --- a/x-pack/plugins/index_management/server/routes/api/indices/register_clear_cache_route.ts +++ b/x-pack/plugins/index_management/server/routes/api/indices/register_clear_cache_route.ts @@ -18,7 +18,7 @@ export function registerClearCacheRoute({ router, lib: { handleEsError } }: Rout router.post( { path: addBasePath('/indices/clear_cache'), validate: { body: bodySchema } }, async (context, request, response) => { - const { client } = context.core.elasticsearch; + const { client } = (await context.core).elasticsearch; const { indices = [] } = request.body as typeof bodySchema.type; const params = { diff --git a/x-pack/plugins/index_management/server/routes/api/indices/register_close_route.ts b/x-pack/plugins/index_management/server/routes/api/indices/register_close_route.ts index 7d549dadd010..69d33b7fc799 100644 --- a/x-pack/plugins/index_management/server/routes/api/indices/register_close_route.ts +++ b/x-pack/plugins/index_management/server/routes/api/indices/register_close_route.ts @@ -18,7 +18,7 @@ export function registerCloseRoute({ router, lib: { handleEsError } }: RouteDepe router.post( { path: addBasePath('/indices/close'), validate: { body: bodySchema } }, async (context, request, response) => { - const { client } = context.core.elasticsearch; + const { client } = (await context.core).elasticsearch; const { indices = [] } = request.body as typeof bodySchema.type; const params = { diff --git a/x-pack/plugins/index_management/server/routes/api/indices/register_delete_route.ts b/x-pack/plugins/index_management/server/routes/api/indices/register_delete_route.ts index 8f9771185805..b72a2059beb1 100644 --- a/x-pack/plugins/index_management/server/routes/api/indices/register_delete_route.ts +++ b/x-pack/plugins/index_management/server/routes/api/indices/register_delete_route.ts @@ -18,7 +18,7 @@ export function registerDeleteRoute({ router, lib: { handleEsError } }: RouteDep router.post( { path: addBasePath('/indices/delete'), validate: { body: bodySchema } }, async (context, request, response) => { - const { client } = context.core.elasticsearch; + const { client } = (await context.core).elasticsearch; const { indices = [] } = request.body as typeof bodySchema.type; const params = { diff --git a/x-pack/plugins/index_management/server/routes/api/indices/register_flush_route.ts b/x-pack/plugins/index_management/server/routes/api/indices/register_flush_route.ts index d8215849d2de..cc07b92e7090 100644 --- a/x-pack/plugins/index_management/server/routes/api/indices/register_flush_route.ts +++ b/x-pack/plugins/index_management/server/routes/api/indices/register_flush_route.ts @@ -18,7 +18,7 @@ export function registerFlushRoute({ router, lib: { handleEsError } }: RouteDepe router.post( { path: addBasePath('/indices/flush'), validate: { body: bodySchema } }, async (context, request, response) => { - const { client } = context.core.elasticsearch; + const { client } = (await context.core).elasticsearch; const { indices = [] } = request.body as typeof bodySchema.type; const params = { diff --git a/x-pack/plugins/index_management/server/routes/api/indices/register_forcemerge_route.ts b/x-pack/plugins/index_management/server/routes/api/indices/register_forcemerge_route.ts index d9bdd572b63f..af07a7371cf6 100644 --- a/x-pack/plugins/index_management/server/routes/api/indices/register_forcemerge_route.ts +++ b/x-pack/plugins/index_management/server/routes/api/indices/register_forcemerge_route.ts @@ -24,7 +24,7 @@ export function registerForcemergeRoute({ router, lib: { handleEsError } }: Rout }, }, async (context, request, response) => { - const { client } = context.core.elasticsearch; + const { client } = (await context.core).elasticsearch; const { maxNumSegments, indices = [] } = request.body as typeof bodySchema.type; const params = { expand_wildcards: 'none' as const, diff --git a/x-pack/plugins/index_management/server/routes/api/indices/register_list_route.ts b/x-pack/plugins/index_management/server/routes/api/indices/register_list_route.ts index cbdfee665cfe..48c1800aba95 100644 --- a/x-pack/plugins/index_management/server/routes/api/indices/register_list_route.ts +++ b/x-pack/plugins/index_management/server/routes/api/indices/register_list_route.ts @@ -17,7 +17,7 @@ export function registerListRoute({ router.get( { path: addBasePath('/indices'), validate: false }, async (context, request, response) => { - const { client } = context.core.elasticsearch; + const { client } = (await context.core).elasticsearch; try { const indices = await fetchIndices(client, indexDataEnricher); return response.ok({ body: indices }); diff --git a/x-pack/plugins/index_management/server/routes/api/indices/register_open_route.ts b/x-pack/plugins/index_management/server/routes/api/indices/register_open_route.ts index b7aa023c2743..dde9e72af39d 100644 --- a/x-pack/plugins/index_management/server/routes/api/indices/register_open_route.ts +++ b/x-pack/plugins/index_management/server/routes/api/indices/register_open_route.ts @@ -18,7 +18,7 @@ export function registerOpenRoute({ router, lib: { handleEsError } }: RouteDepen router.post( { path: addBasePath('/indices/open'), validate: { body: bodySchema } }, async (context, request, response) => { - const { client } = context.core.elasticsearch; + const { client } = (await context.core).elasticsearch; const { indices = [] } = request.body as typeof bodySchema.type; const params = { diff --git a/x-pack/plugins/index_management/server/routes/api/indices/register_refresh_route.ts b/x-pack/plugins/index_management/server/routes/api/indices/register_refresh_route.ts index ba7d3f19dbb2..2483cd534b80 100644 --- a/x-pack/plugins/index_management/server/routes/api/indices/register_refresh_route.ts +++ b/x-pack/plugins/index_management/server/routes/api/indices/register_refresh_route.ts @@ -18,7 +18,7 @@ export function registerRefreshRoute({ router, lib: { handleEsError } }: RouteDe router.post( { path: addBasePath('/indices/refresh'), validate: { body: bodySchema } }, async (context, request, response) => { - const { client } = context.core.elasticsearch; + const { client } = (await context.core).elasticsearch; const { indices = [] } = request.body as typeof bodySchema.type; const params = { diff --git a/x-pack/plugins/index_management/server/routes/api/indices/register_reload_route.ts b/x-pack/plugins/index_management/server/routes/api/indices/register_reload_route.ts index 05951629bcb2..42b78fe246ba 100644 --- a/x-pack/plugins/index_management/server/routes/api/indices/register_reload_route.ts +++ b/x-pack/plugins/index_management/server/routes/api/indices/register_reload_route.ts @@ -25,7 +25,7 @@ export function registerReloadRoute({ router.post( { path: addBasePath('/indices/reload'), validate: { body: bodySchema } }, async (context, request, response) => { - const { client } = context.core.elasticsearch; + const { client } = (await context.core).elasticsearch; const { indexNames = [] } = (request.body as typeof bodySchema.type) ?? {}; try { diff --git a/x-pack/plugins/index_management/server/routes/api/indices/register_unfreeze_route.ts b/x-pack/plugins/index_management/server/routes/api/indices/register_unfreeze_route.ts index e0447efe1090..3636a0707df8 100644 --- a/x-pack/plugins/index_management/server/routes/api/indices/register_unfreeze_route.ts +++ b/x-pack/plugins/index_management/server/routes/api/indices/register_unfreeze_route.ts @@ -18,7 +18,7 @@ export function registerUnfreezeRoute({ router, lib: { handleEsError } }: RouteD router.post( { path: addBasePath('/indices/unfreeze'), validate: { body: bodySchema } }, async (context, request, response) => { - const { client } = context.core.elasticsearch; + const { client } = (await context.core).elasticsearch; const { indices = [] } = request.body as typeof bodySchema.type; try { diff --git a/x-pack/plugins/index_management/server/routes/api/mapping/register_mapping_route.ts b/x-pack/plugins/index_management/server/routes/api/mapping/register_mapping_route.ts index c2dbcc92144b..641ad26417f6 100644 --- a/x-pack/plugins/index_management/server/routes/api/mapping/register_mapping_route.ts +++ b/x-pack/plugins/index_management/server/routes/api/mapping/register_mapping_route.ts @@ -25,7 +25,7 @@ export function registerMappingRoute({ router, lib: { handleEsError } }: RouteDe router.get( { path: addBasePath('/mapping/{indexName}'), validate: { params: paramsSchema } }, async (context, request, response) => { - const { client } = context.core.elasticsearch; + const { client } = (await context.core).elasticsearch; const { indexName } = request.params as typeof paramsSchema.type; const params = { expand_wildcards: 'none' as const, diff --git a/x-pack/plugins/index_management/server/routes/api/nodes/register_nodes_route.ts b/x-pack/plugins/index_management/server/routes/api/nodes/register_nodes_route.ts index 8702a6bdd203..08a0684f69c9 100644 --- a/x-pack/plugins/index_management/server/routes/api/nodes/register_nodes_route.ts +++ b/x-pack/plugins/index_management/server/routes/api/nodes/register_nodes_route.ts @@ -13,7 +13,7 @@ export function registerNodesRoute({ router, lib: { handleEsError } }: RouteDepe router.get( { path: addBasePath('/nodes/plugins'), validate: {} }, async (context, request, response) => { - const { client } = context.core.elasticsearch; + const { client } = (await context.core).elasticsearch; try { const body = await client.asCurrentUser.nodes.info(); diff --git a/x-pack/plugins/index_management/server/routes/api/settings/register_load_route.ts b/x-pack/plugins/index_management/server/routes/api/settings/register_load_route.ts index fc8600182dd1..bcdf29af6585 100644 --- a/x-pack/plugins/index_management/server/routes/api/settings/register_load_route.ts +++ b/x-pack/plugins/index_management/server/routes/api/settings/register_load_route.ts @@ -25,7 +25,7 @@ export function registerLoadRoute({ router, lib: { handleEsError } }: RouteDepen router.get( { path: addBasePath('/settings/{indexName}'), validate: { params: paramsSchema } }, async (context, request, response) => { - const { client } = context.core.elasticsearch; + const { client } = (await context.core).elasticsearch; const { indexName } = request.params as typeof paramsSchema.type; const params = { expand_wildcards: 'none' as const, diff --git a/x-pack/plugins/index_management/server/routes/api/settings/register_update_route.ts b/x-pack/plugins/index_management/server/routes/api/settings/register_update_route.ts index 31f04a20c5db..cfb0be50e048 100644 --- a/x-pack/plugins/index_management/server/routes/api/settings/register_update_route.ts +++ b/x-pack/plugins/index_management/server/routes/api/settings/register_update_route.ts @@ -23,7 +23,7 @@ export function registerUpdateRoute({ router, lib: { handleEsError } }: RouteDep validate: { body: bodySchema, params: paramsSchema }, }, async (context, request, response) => { - const { client } = context.core.elasticsearch; + const { client } = (await context.core).elasticsearch; const { indexName } = request.params as typeof paramsSchema.type; const params = { ignore_unavailable: true, diff --git a/x-pack/plugins/index_management/server/routes/api/stats/register_stats_route.ts b/x-pack/plugins/index_management/server/routes/api/stats/register_stats_route.ts index a81aba00901e..8e361d8488ba 100644 --- a/x-pack/plugins/index_management/server/routes/api/stats/register_stats_route.ts +++ b/x-pack/plugins/index_management/server/routes/api/stats/register_stats_route.ts @@ -33,7 +33,7 @@ export function registerStatsRoute({ router, lib: { handleEsError } }: RouteDepe router.get( { path: addBasePath('/stats/{indexName}'), validate: { params: paramsSchema } }, async (context, request, response) => { - const { client } = context.core.elasticsearch; + const { client } = (await context.core).elasticsearch; const { indexName } = request.params as typeof paramsSchema.type; const params = { expand_wildcards: 'none' as const, diff --git a/x-pack/plugins/index_management/server/routes/api/templates/register_create_route.ts b/x-pack/plugins/index_management/server/routes/api/templates/register_create_route.ts index 7463f41cb18a..0b903d4356ec 100644 --- a/x-pack/plugins/index_management/server/routes/api/templates/register_create_route.ts +++ b/x-pack/plugins/index_management/server/routes/api/templates/register_create_route.ts @@ -19,7 +19,7 @@ export function registerCreateRoute({ router, lib: { handleEsError } }: RouteDep router.post( { path: addBasePath('/index_templates'), validate: { body: bodySchema } }, async (context, request, response) => { - const { client } = context.core.elasticsearch; + const { client } = (await context.core).elasticsearch; const template = request.body as TemplateDeserialized; try { diff --git a/x-pack/plugins/index_management/server/routes/api/templates/register_delete_route.ts b/x-pack/plugins/index_management/server/routes/api/templates/register_delete_route.ts index 585408bbfe23..b6c289f3a72a 100644 --- a/x-pack/plugins/index_management/server/routes/api/templates/register_delete_route.ts +++ b/x-pack/plugins/index_management/server/routes/api/templates/register_delete_route.ts @@ -28,7 +28,7 @@ export function registerDeleteRoute({ router, lib: { handleEsError } }: RouteDep validate: { body: bodySchema }, }, async (context, request, response) => { - const { client } = context.core.elasticsearch; + const { client } = (await context.core).elasticsearch; const { templates } = request.body as TypeOf; const responseBody: { templatesDeleted: Array; diff --git a/x-pack/plugins/index_management/server/routes/api/templates/register_get_routes.ts b/x-pack/plugins/index_management/server/routes/api/templates/register_get_routes.ts index f8966b1355ff..32661bb30887 100644 --- a/x-pack/plugins/index_management/server/routes/api/templates/register_get_routes.ts +++ b/x-pack/plugins/index_management/server/routes/api/templates/register_get_routes.ts @@ -21,7 +21,7 @@ export function registerGetAllRoute({ router, lib: { handleEsError } }: RouteDep router.get( { path: addBasePath('/index_templates'), validate: false }, async (context, request, response) => { - const { client } = context.core.elasticsearch; + const { client } = (await context.core).elasticsearch; try { const cloudManagedTemplatePrefix = await getCloudManagedTemplatePrefix(client); @@ -66,7 +66,7 @@ export function registerGetOneRoute({ router, lib: { handleEsError } }: RouteDep validate: { params: paramsSchema, query: querySchema }, }, async (context, request, response) => { - const { client } = context.core.elasticsearch; + const { client } = (await context.core).elasticsearch; const { name } = request.params as TypeOf; const isLegacy = (request.query as TypeOf).legacy === 'true'; diff --git a/x-pack/plugins/index_management/server/routes/api/templates/register_simulate_route.ts b/x-pack/plugins/index_management/server/routes/api/templates/register_simulate_route.ts index 13833cb88936..3dc6201f0831 100644 --- a/x-pack/plugins/index_management/server/routes/api/templates/register_simulate_route.ts +++ b/x-pack/plugins/index_management/server/routes/api/templates/register_simulate_route.ts @@ -19,7 +19,7 @@ export function registerSimulateRoute({ router, lib: { handleEsError } }: RouteD validate: { body: bodySchema }, }, async (context, request, response) => { - const { client } = context.core.elasticsearch; + const { client } = (await context.core).elasticsearch; const template = request.body as TypeOf; try { diff --git a/x-pack/plugins/index_management/server/routes/api/templates/register_update_route.ts b/x-pack/plugins/index_management/server/routes/api/templates/register_update_route.ts index 7d89d0ff8af0..30b93f2e59ec 100644 --- a/x-pack/plugins/index_management/server/routes/api/templates/register_update_route.ts +++ b/x-pack/plugins/index_management/server/routes/api/templates/register_update_route.ts @@ -25,7 +25,7 @@ export function registerUpdateRoute({ router, lib: { handleEsError } }: RouteDep validate: { body: bodySchema, params: paramsSchema }, }, async (context, request, response) => { - const { client } = context.core.elasticsearch; + const { client } = (await context.core).elasticsearch; const { name } = request.params as typeof paramsSchema.type; const template = request.body as TemplateDeserialized; diff --git a/x-pack/plugins/infra/public/components/autocomplete_field/autocomplete_field.tsx b/x-pack/plugins/infra/public/components/autocomplete_field/autocomplete_field.tsx index 82f5516f0831..d54b12511a05 100644 --- a/x-pack/plugins/infra/public/components/autocomplete_field/autocomplete_field.tsx +++ b/x-pack/plugins/infra/public/components/autocomplete_field/autocomplete_field.tsx @@ -7,7 +7,7 @@ import { EuiFieldSearch, EuiOutsideClickDetector, EuiPanel } from '@elastic/eui'; import React from 'react'; -import { QuerySuggestion } from '@kbn/data-plugin/public'; +import { QuerySuggestion } from '@kbn/unified-search-plugin/public'; import { euiStyled } from '@kbn/kibana-react-plugin/common'; import { composeStateUpdaters } from '../../utils/typed_react'; import { SuggestionItem } from './suggestion_item'; diff --git a/x-pack/plugins/infra/public/components/autocomplete_field/suggestion_item.tsx b/x-pack/plugins/infra/public/components/autocomplete_field/suggestion_item.tsx index 47a5114f7917..a317ad3ecff5 100644 --- a/x-pack/plugins/infra/public/components/autocomplete_field/suggestion_item.tsx +++ b/x-pack/plugins/infra/public/components/autocomplete_field/suggestion_item.tsx @@ -5,11 +5,11 @@ * 2.0. */ -import { EuiIcon } from '@elastic/eui'; -import { transparentize } from 'polished'; import React from 'react'; +import { EuiIcon } from '@elastic/eui'; import { euiStyled } from '@kbn/kibana-react-plugin/common'; -import { QuerySuggestion, QuerySuggestionTypes } from '@kbn/data-plugin/public'; +import { QuerySuggestion, QuerySuggestionTypes } from '@kbn/unified-search-plugin/public'; +import { transparentize } from 'polished'; interface Props { isSelected?: boolean; diff --git a/x-pack/plugins/infra/public/containers/with_kuery_autocompletion.tsx b/x-pack/plugins/infra/public/containers/with_kuery_autocompletion.tsx index 5da13a4fed71..96410973f8c0 100644 --- a/x-pack/plugins/infra/public/containers/with_kuery_autocompletion.tsx +++ b/x-pack/plugins/infra/public/containers/with_kuery_autocompletion.tsx @@ -7,16 +7,18 @@ import React from 'react'; import { DataViewBase } from '@kbn/es-query'; -import { QuerySuggestion, DataPublicPluginStart } from '@kbn/data-plugin/public'; import { withKibana, KibanaReactContextValue, KibanaServices, } from '@kbn/kibana-react-plugin/public'; +import { QuerySuggestion, UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; import { RendererFunction } from '../utils/typed_react'; interface WithKueryAutocompletionLifecycleProps { - kibana: KibanaReactContextValue<{ data: DataPublicPluginStart } & KibanaServices>; + kibana: KibanaReactContextValue< + { unifiedSearch: UnifiedSearchPublicPluginStart } & KibanaServices + >; children: RendererFunction<{ isLoadingSuggestions: boolean; loadSuggestions: (expression: string, cursorPosition: number, maxSuggestions?: number) => void; @@ -63,7 +65,7 @@ class WithKueryAutocompletionComponent extends React.Component< const { indexPattern } = this.props; const language = 'kuery'; const hasQuerySuggestions = - this.props.kibana.services.data?.autocomplete.hasQuerySuggestions(language); + this.props.kibana.services.unifiedSearch?.autocomplete.hasQuerySuggestions(language); if (!hasQuerySuggestions) { return; @@ -78,7 +80,7 @@ class WithKueryAutocompletionComponent extends React.Component< }); const suggestions = - (await this.props.kibana.services.data.autocomplete.getQuerySuggestions({ + (await this.props.kibana.services.unifiedSearch.autocomplete.getQuerySuggestions({ language, query: expression, selectionStart: cursorPosition, diff --git a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/kuery_bar.tsx b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/kuery_bar.tsx index ff3cbdceefb3..0e7d4e6d41e1 100644 --- a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/kuery_bar.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/kuery_bar.tsx @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import { fromKueryExpression } from '@kbn/es-query'; import React, { useEffect, useState } from 'react'; import { DataViewBase } from '@kbn/es-query'; -import { QuerySuggestion } from '@kbn/data-plugin/public'; +import { QuerySuggestion } from '@kbn/unified-search-plugin/public'; import { WithKueryAutocompletion } from '../../../../containers/with_kuery_autocompletion'; import { AutocompleteField } from '../../../../components/autocomplete_field'; diff --git a/x-pack/plugins/infra/server/lib/adapters/framework/kibana_framework_adapter.ts b/x-pack/plugins/infra/server/lib/adapters/framework/kibana_framework_adapter.ts index 28592e430c16..7aa4b433c477 100644 --- a/x-pack/plugins/infra/server/lib/adapters/framework/kibana_framework_adapter.ts +++ b/x-pack/plugins/infra/server/lib/adapters/framework/kibana_framework_adapter.ts @@ -124,7 +124,7 @@ export class KibanaFramework { endpoint: string, params: CallWithRequestParams ) { - const { elasticsearch, uiSettings } = requestContext.core; + const { elasticsearch, uiSettings } = await requestContext.core; const includeFrozen = await uiSettings.client.get(UI_SETTINGS.SEARCH_INCLUDE_FROZEN); if (endpoint === 'msearch') { @@ -200,9 +200,10 @@ export class KibanaFramework { public async getIndexPatternsServiceWithRequestContext( requestContext: InfraPluginRequestHandlerContext ) { + const { savedObjects, elasticsearch } = await requestContext.core; return await this.createIndexPatternsService( - requestContext.core.savedObjects.client, - requestContext.core.elasticsearch.client.asCurrentUser + savedObjects.client, + elasticsearch.client.asCurrentUser ); } diff --git a/x-pack/plugins/infra/server/lib/domains/fields_domain.ts b/x-pack/plugins/infra/server/lib/domains/fields_domain.ts index 0997d9517fa8..b80fa9d79602 100644 --- a/x-pack/plugins/infra/server/lib/domains/fields_domain.ts +++ b/x-pack/plugins/infra/server/lib/domains/fields_domain.ts @@ -20,10 +20,8 @@ export class InfraFieldsDomain { sourceId: string, indexType: 'METRICS' ): Promise { - const { configuration } = await this.libs.sources.getSourceConfiguration( - requestContext.core.savedObjects.client, - sourceId - ); + 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); diff --git a/x-pack/plugins/infra/server/lib/domains/log_entries_domain/log_entries_domain.ts b/x-pack/plugins/infra/server/lib/domains/log_entries_domain/log_entries_domain.ts index ef6b7122aa4d..1e0daad376a1 100644 --- a/x-pack/plugins/infra/server/lib/domains/log_entries_domain/log_entries_domain.ts +++ b/x-pack/plugins/infra/server/lib/domains/log_entries_domain/log_entries_domain.ts @@ -42,6 +42,7 @@ export interface LogEntriesParams { cursor?: { before: LogEntryCursor | 'last' } | { after: LogEntryCursor | 'first' }; highlightTerm?: string; } + export interface LogEntriesAroundParams { startTimestamp: number; endTimestamp: number; @@ -130,11 +131,9 @@ export class InfraLogEntriesDomain { columnOverrides?: LogViewColumnConfiguration[] ): Promise<{ entries: LogEntry[]; hasMoreBefore?: boolean; hasMoreAfter?: boolean }> { const [, , { logViews }] = await this.libs.getStartServices(); + const { savedObjects, elasticsearch } = await requestContext.core; const resolvedLogView = await logViews - .getClient( - requestContext.core.savedObjects.client, - requestContext.core.elasticsearch.client.asCurrentUser - ) + .getClient(savedObjects.client, elasticsearch.client.asCurrentUser) .getResolvedLogView(sourceId); const columnDefinitions = columnOverrides ?? resolvedLogView.columns; @@ -192,11 +191,9 @@ export class InfraLogEntriesDomain { filterQuery?: LogEntryQuery ): Promise { const [, , { logViews }] = await this.libs.getStartServices(); + const { savedObjects, elasticsearch } = await requestContext.core; const resolvedLogView = await logViews - .getClient( - requestContext.core.savedObjects.client, - requestContext.core.elasticsearch.client.asCurrentUser - ) + .getClient(savedObjects.client, elasticsearch.client.asCurrentUser) .getResolvedLogView(sourceId); const dateRangeBuckets = await this.adapter.getContainedLogSummaryBuckets( requestContext, @@ -219,11 +216,9 @@ export class InfraLogEntriesDomain { filterQuery?: LogEntryQuery ): Promise { const [, , { logViews }] = await this.libs.getStartServices(); + const { savedObjects, elasticsearch } = await requestContext.core; const resolvedLogView = await logViews - .getClient( - requestContext.core.savedObjects.client, - requestContext.core.elasticsearch.client.asCurrentUser - ) + .getClient(savedObjects.client, elasticsearch.client.asCurrentUser) .getResolvedLogView(sourceId); const messageFormattingRules = compileFormattingRules( getBuiltinRules(resolvedLogView.messageField) diff --git a/x-pack/plugins/infra/server/lib/log_analysis/log_entry_anomalies.ts b/x-pack/plugins/infra/server/lib/log_analysis/log_entry_anomalies.ts index feb0f6b5b999..22bf5466ee3b 100644 --- a/x-pack/plugins/infra/server/lib/log_analysis/log_entry_anomalies.ts +++ b/x-pack/plugins/infra/server/lib/log_analysis/log_entry_anomalies.ts @@ -96,7 +96,9 @@ async function getCompatibleAnomaliesJobIds( } export async function getLogEntryAnomalies( - context: InfraPluginRequestHandlerContext & { infra: Required }, + context: InfraPluginRequestHandlerContext & { + infra: Promise>; + }, sourceId: string, startTime: number, endTime: number, @@ -106,13 +108,14 @@ export async function getLogEntryAnomalies( ) { const finalizeLogEntryAnomaliesSpan = startTracingSpan('get log entry anomalies'); + const infraContext = await context.infra; const { jobIds, timing: { spans: jobSpans }, } = await getCompatibleAnomaliesJobIds( - context.infra.spaceId, + infraContext.spaceId, sourceId, - context.infra.mlAnomalyDetectors + infraContext.mlAnomalyDetectors ); if (jobIds.length === 0) { @@ -127,7 +130,7 @@ export async function getLogEntryAnomalies( hasMoreEntries, timing: { spans: fetchLogEntryAnomaliesSpans }, } = await fetchLogEntryAnomalies( - context.infra.mlSystem, + infraContext.mlSystem, jobIds, startTime, endTime, @@ -151,13 +154,13 @@ export async function getLogEntryAnomalies( }, []); const logEntryCategoriesCountJobId = getJobId( - context.infra.spaceId, + infraContext.spaceId, sourceId, logEntryCategoriesJobTypes[0] ); const { logEntryCategoriesById } = await fetchLogEntryCategories( - context, + { infra: infraContext }, logEntryCategoriesCountJobId, categoryIds ); @@ -325,7 +328,9 @@ async function fetchLogEntryAnomalies( } export async function getLogEntryExamples( - context: InfraPluginRequestHandlerContext & { infra: Required }, + context: InfraPluginRequestHandlerContext & { + infra: Promise>; + }, sourceId: string, startTime: number, endTime: number, @@ -336,9 +341,10 @@ export async function getLogEntryExamples( categoryId?: string ) { const finalizeLogEntryExamplesSpan = startTracingSpan('get log entry rate example log entries'); + const infraContext = await context.infra; const jobId = getJobId( - context.infra.spaceId, + infraContext.spaceId, sourceId, categoryId != null ? logEntryCategoriesJobTypes[0] : logEntryRateJobTypes[0] ); @@ -346,7 +352,7 @@ export async function getLogEntryExamples( const { mlJob, timing: { spans: fetchMlJobSpans }, - } = await fetchMlJob(context.infra.mlAnomalyDetectors, jobId); + } = await fetchMlJob(infraContext.mlAnomalyDetectors, jobId); const customSettings = decodeOrThrow(jobCustomSettingsRT)(mlJob.custom_settings); const indices = customSettings?.logs_source_config?.indexPattern; @@ -388,7 +394,9 @@ export async function getLogEntryExamples( } export async function fetchLogEntryExamples( - context: InfraPluginRequestHandlerContext & { infra: Required }, + context: InfraPluginRequestHandlerContext & { + infra: Promise>; + }, sourceId: string, indices: string, runtimeMappings: estypes.MappingRuntimeFields, @@ -403,6 +411,7 @@ export async function fetchLogEntryExamples( ) { const finalizeEsSearchSpan = startTracingSpan('Fetch log rate examples from ES'); + const infraContext = await context.infra; let categoryQuery: string | undefined; // Examples should be further scoped to a specific ML category @@ -410,13 +419,13 @@ export async function fetchLogEntryExamples( const parsedCategoryId = parseInt(categoryId, 10); const logEntryCategoriesCountJobId = getJobId( - context.infra.spaceId, + infraContext.spaceId, sourceId, logEntryCategoriesJobTypes[0] ); const { logEntryCategoriesById } = await fetchLogEntryCategories( - context, + { infra: infraContext }, logEntryCategoriesCountJobId, [parsedCategoryId] ); diff --git a/x-pack/plugins/infra/server/lib/source_status.ts b/x-pack/plugins/infra/server/lib/source_status.ts index 8c6d7d1ad451..9e492e448ab9 100644 --- a/x-pack/plugins/infra/server/lib/source_status.ts +++ b/x-pack/plugins/infra/server/lib/source_status.ts @@ -18,38 +18,34 @@ export class InfraSourceStatus { requestContext: InfraPluginRequestHandlerContext, sourceId: string ): Promise { - const sourceConfiguration = await this.libs.sources.getSourceConfiguration( - requestContext.core.savedObjects.client, - sourceId - ); + const soClient = (await requestContext.core).savedObjects.client; + const sourceConfiguration = await this.libs.sources.getSourceConfiguration(soClient, sourceId); const indexNames = await this.adapter.getIndexNames( requestContext, sourceConfiguration.configuration.metricAlias ); return indexNames; } + public async hasMetricAlias( requestContext: InfraPluginRequestHandlerContext, sourceId: string ): Promise { - const sourceConfiguration = await this.libs.sources.getSourceConfiguration( - requestContext.core.savedObjects.client, - sourceId - ); + const soClient = (await requestContext.core).savedObjects.client; + const sourceConfiguration = await this.libs.sources.getSourceConfiguration(soClient, sourceId); const hasAlias = await this.adapter.hasAlias( requestContext, sourceConfiguration.configuration.metricAlias ); return hasAlias; } + public async hasMetricIndices( requestContext: InfraPluginRequestHandlerContext, sourceId: string ): Promise { - const sourceConfiguration = await this.libs.sources.getSourceConfiguration( - requestContext.core.savedObjects.client, - sourceId - ); + const soClient = (await requestContext.core).savedObjects.client; + const sourceConfiguration = await this.libs.sources.getSourceConfiguration(soClient, sourceId); const indexStatus = await this.adapter.getIndexStatus( requestContext, sourceConfiguration.configuration.metricAlias @@ -65,7 +61,9 @@ export interface InfraSourceStatusAdapter { requestContext: InfraPluginRequestHandlerContext, aliasName: string ): Promise; + hasAlias(requestContext: InfraPluginRequestHandlerContext, aliasName: string): Promise; + getIndexStatus( requestContext: InfraPluginRequestHandlerContext, indexNames: string diff --git a/x-pack/plugins/infra/server/plugin.ts b/x-pack/plugins/infra/server/plugin.ts index a0d0b617f3f4..8eb2bac57dc5 100644 --- a/x-pack/plugins/infra/server/plugin.ts +++ b/x-pack/plugins/infra/server/plugin.ts @@ -190,12 +190,10 @@ export class InfraServerPlugin core.http.registerRouteHandlerContext( 'infra', - (context, request) => { - const mlSystem = plugins.ml?.mlSystemProvider(request, context.core.savedObjects.client); - const mlAnomalyDetectors = plugins.ml?.anomalyDetectorsProvider( - request, - context.core.savedObjects.client - ); + async (context, request) => { + const soClient = (await context.core).savedObjects.client; + const mlSystem = plugins.ml?.mlSystemProvider(request, soClient); + const mlAnomalyDetectors = plugins.ml?.anomalyDetectorsProvider(request, soClient); const spaceId = plugins.spaces?.spacesService.getSpaceId(request) || 'default'; return { @@ -240,5 +238,6 @@ export class InfraServerPlugin logViews, }; } + stop() {} } diff --git a/x-pack/plugins/infra/server/routes/infra_ml/results/metrics_hosts_anomalies.ts b/x-pack/plugins/infra/server/routes/infra_ml/results/metrics_hosts_anomalies.ts index 1b5ed62a006b..a83854817866 100644 --- a/x-pack/plugins/infra/server/routes/infra_ml/results/metrics_hosts_anomalies.ts +++ b/x-pack/plugins/infra/server/routes/infra_ml/results/metrics_hosts_anomalies.ts @@ -47,7 +47,7 @@ export const initGetHostsAnomaliesRoute = ({ framework }: InfraBackendLibs) => { const { sort, pagination } = getSortAndPagination(sortParam, paginationParam); try { - assertHasInfraMlPlugins(requestContext); + const infraMlContext = await assertHasInfraMlPlugins(requestContext); const { data: anomalies, @@ -55,7 +55,7 @@ export const initGetHostsAnomaliesRoute = ({ framework }: InfraBackendLibs) => { hasMoreEntries, timing, } = await getMetricsHostsAnomalies({ - context: requestContext.infra, + context: await infraMlContext.infra, sourceId, anomalyThreshold, startTime, diff --git a/x-pack/plugins/infra/server/routes/infra_ml/results/metrics_k8s_anomalies.ts b/x-pack/plugins/infra/server/routes/infra_ml/results/metrics_k8s_anomalies.ts index 667760d1d409..55cc6c409832 100644 --- a/x-pack/plugins/infra/server/routes/infra_ml/results/metrics_k8s_anomalies.ts +++ b/x-pack/plugins/infra/server/routes/infra_ml/results/metrics_k8s_anomalies.ts @@ -45,7 +45,7 @@ export const initGetK8sAnomaliesRoute = ({ framework }: InfraBackendLibs) => { const { sort, pagination } = getSortAndPagination(sortParam, paginationParam); try { - assertHasInfraMlPlugins(requestContext); + const infraMlContext = await assertHasInfraMlPlugins(requestContext); const { data: anomalies, @@ -53,7 +53,7 @@ export const initGetK8sAnomaliesRoute = ({ framework }: InfraBackendLibs) => { hasMoreEntries, timing, } = await getMetricK8sAnomalies({ - context: requestContext.infra, + context: await infraMlContext.infra, sourceId, anomalyThreshold, startTime, diff --git a/x-pack/plugins/infra/server/routes/inventory_metadata/index.ts b/x-pack/plugins/infra/server/routes/inventory_metadata/index.ts index 70d29773f76c..c6ed81ccd825 100644 --- a/x-pack/plugins/infra/server/routes/inventory_metadata/index.ts +++ b/x-pack/plugins/infra/server/routes/inventory_metadata/index.ts @@ -38,10 +38,8 @@ export const initInventoryMetaRoute = (libs: InfraBackendLibs) => { fold(throwErrors(Boom.badRequest), identity) ); - const { configuration } = await libs.sources.getSourceConfiguration( - requestContext.core.savedObjects.client, - sourceId - ); + const soClient = (await requestContext.core).savedObjects.client; + const { configuration } = await libs.sources.getSourceConfiguration(soClient, sourceId); const awsMetadata = await getCloudMetadata( framework, diff --git a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_anomalies.ts b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_anomalies.ts index d0a18727b681..dd6254cf560e 100644 --- a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_anomalies.ts +++ b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_anomalies.ts @@ -42,7 +42,7 @@ export const initGetLogEntryAnomaliesRoute = ({ framework }: InfraBackendLibs) = const { sort, pagination } = getSortAndPagination(sortParam, paginationParam); try { - assertHasInfraMlPlugins(requestContext); + const infraMlContext = await assertHasInfraMlPlugins(requestContext); const { data: logEntryAnomalies, @@ -50,7 +50,7 @@ export const initGetLogEntryAnomaliesRoute = ({ framework }: InfraBackendLibs) = hasMoreEntries, timing, } = await getLogEntryAnomalies( - requestContext, + infraMlContext, sourceId, startTime, endTime, diff --git a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_anomalies_datasets.ts b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_anomalies_datasets.ts index 9d3b9f22a839..1d1f620063b2 100644 --- a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_anomalies_datasets.ts +++ b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_anomalies_datasets.ts @@ -35,10 +35,10 @@ export const initGetLogEntryAnomaliesDatasetsRoute = ({ framework }: InfraBacken } = request.body; try { - assertHasInfraMlPlugins(requestContext); + const infraMlContext = await assertHasInfraMlPlugins(requestContext); const { datasets, timing } = await getLogEntryAnomaliesDatasets( - requestContext, + { infra: await infraMlContext.infra }, sourceId, startTime, endTime diff --git a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_categories.ts b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_categories.ts index d3660aa16c1f..6e2e8e8a6c2a 100644 --- a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_categories.ts +++ b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_categories.ts @@ -39,10 +39,10 @@ export const initGetLogEntryCategoriesRoute = ({ framework }: InfraBackendLibs) } = request.body; try { - assertHasInfraMlPlugins(requestContext); + const infraMlContext = await assertHasInfraMlPlugins(requestContext); const { data: topLogEntryCategories, timing } = await getTopLogEntryCategories( - requestContext, + { infra: await infraMlContext.infra }, sourceId, startTime, endTime, diff --git a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_datasets.ts b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_datasets.ts index 76103c936dd6..de5ac9dac4b0 100644 --- a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_datasets.ts +++ b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_datasets.ts @@ -35,10 +35,10 @@ export const initGetLogEntryCategoryDatasetsRoute = ({ framework }: InfraBackend } = request.body; try { - assertHasInfraMlPlugins(requestContext); + const infraMlContext = await assertHasInfraMlPlugins(requestContext); const { data: logEntryCategoryDatasets, timing } = await getLogEntryCategoryDatasets( - requestContext, + { infra: await infraMlContext.infra }, sourceId, startTime, endTime diff --git a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_datasets_stats.ts b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_datasets_stats.ts index 01a98ce43833..5844405ec062 100644 --- a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_datasets_stats.ts +++ b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_datasets_stats.ts @@ -36,10 +36,10 @@ export const initGetLogEntryCategoryDatasetsStatsRoute = ({ framework }: InfraBa } = request.body; try { - assertHasInfraMlPlugins(requestContext); + const infraMlContext = await assertHasInfraMlPlugins(requestContext); const { data: datasetStats, timing } = await getLatestLogEntriesCategoriesDatasetsStats( - requestContext, + { infra: await infraMlContext.infra }, jobIds, startTime, endTime, diff --git a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_examples.ts b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_examples.ts index 14787406319d..b51aed45b7e1 100644 --- a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_examples.ts +++ b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_examples.ts @@ -43,10 +43,10 @@ export const initGetLogEntryCategoryExamplesRoute = ({ const resolvedLogView = await logViews.getScopedClient(request).getResolvedLogView(sourceId); try { - assertHasInfraMlPlugins(requestContext); + const infraMlContext = await assertHasInfraMlPlugins(requestContext); const { data: logEntryCategoryExamples, timing } = await getLogEntryCategoryExamples( - requestContext, + { infra: await infraMlContext.infra, core: await infraMlContext.core }, sourceId, startTime, endTime, diff --git a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_examples.ts b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_examples.ts index f11e9e46bf4c..fb82a2cd90df 100644 --- a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_examples.ts +++ b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_examples.ts @@ -44,10 +44,10 @@ export const initGetLogEntryExamplesRoute = ({ const resolvedLogView = await logViews.getScopedClient(request).getResolvedLogView(sourceId); try { - assertHasInfraMlPlugins(requestContext); + const infraMlContext = await assertHasInfraMlPlugins(requestContext); const { data: logEntryExamples, timing } = await getLogEntryExamples( - requestContext, + infraMlContext, sourceId, startTime, endTime, diff --git a/x-pack/plugins/infra/server/routes/metadata/index.ts b/x-pack/plugins/infra/server/routes/metadata/index.ts index 39021ba51c9d..18ee48ed5ad0 100644 --- a/x-pack/plugins/infra/server/routes/metadata/index.ts +++ b/x-pack/plugins/infra/server/routes/metadata/index.ts @@ -42,10 +42,8 @@ export const initMetadataRoute = (libs: InfraBackendLibs) => { fold(throwErrors(Boom.badRequest), identity) ); - const { configuration } = await libs.sources.getSourceConfiguration( - requestContext.core.savedObjects.client, - sourceId - ); + const soClient = (await requestContext.core).savedObjects.client; + const { configuration } = await libs.sources.getSourceConfiguration(soClient, sourceId); const metricsMetadata = await getMetricMetadata( framework, requestContext, diff --git a/x-pack/plugins/infra/server/routes/metrics_sources/index.ts b/x-pack/plugins/infra/server/routes/metrics_sources/index.ts index cba520e6d53c..a0cd0afbca50 100644 --- a/x-pack/plugins/infra/server/routes/metrics_sources/index.ts +++ b/x-pack/plugins/infra/server/routes/metrics_sources/index.ts @@ -33,9 +33,10 @@ export const initMetricsSourceConfigurationRoutes = (libs: InfraBackendLibs) => }, async (requestContext, request, response) => { const { sourceId } = request.params; + const soClient = (await requestContext.core).savedObjects.client; const [source, metricIndicesExist, indexFields] = await Promise.all([ - libs.sources.getSourceConfiguration(requestContext.core.savedObjects.client, sourceId), + libs.sources.getSourceConfiguration(soClient, sourceId), libs.sourceStatus.hasMetricIndices(requestContext, sourceId), libs.fields.getFields(requestContext, sourceId, 'METRICS'), ]); @@ -72,10 +73,8 @@ export const initMetricsSourceConfigurationRoutes = (libs: InfraBackendLibs) => const patchedSourceConfigurationProperties = request.body; try { - const sourceConfiguration = await sources.getSourceConfiguration( - requestContext.core.savedObjects.client, - sourceId - ); + const soClient = (await requestContext.core).savedObjects.client; + const sourceConfiguration = await sources.getSourceConfiguration(soClient, sourceId); if (sourceConfiguration.origin === 'internal') { response.conflict({ @@ -86,13 +85,13 @@ export const initMetricsSourceConfigurationRoutes = (libs: InfraBackendLibs) => const sourceConfigurationExists = sourceConfiguration.origin === 'stored'; const patchedSourceConfiguration = await (sourceConfigurationExists ? sources.updateSourceConfiguration( - requestContext.core.savedObjects.client, + soClient, sourceId, // @ts-ignore patchedSourceConfigurationProperties ) : sources.createSourceConfiguration( - requestContext.core.savedObjects.client, + soClient, sourceId, // @ts-ignore patchedSourceConfigurationProperties @@ -151,10 +150,8 @@ export const initMetricsSourceConfigurationRoutes = (libs: InfraBackendLibs) => const { sourceId } = request.params; const client = createSearchClient(requestContext, framework); - const source = await libs.sources.getSourceConfiguration( - requestContext.core.savedObjects.client, - sourceId - ); + const soClient = (await requestContext.core).savedObjects.client; + const source = await libs.sources.getSourceConfiguration(soClient, sourceId); const results = await hasData(source.configuration.metricAlias, client); diff --git a/x-pack/plugins/infra/server/routes/node_details/index.ts b/x-pack/plugins/infra/server/routes/node_details/index.ts index 8e305226112b..cd92c902a110 100644 --- a/x-pack/plugins/infra/server/routes/node_details/index.ts +++ b/x-pack/plugins/infra/server/routes/node_details/index.ts @@ -38,10 +38,8 @@ export const initNodeDetailsRoute = (libs: InfraBackendLibs) => { NodeDetailsRequestRT.decode(request.body), fold(throwErrors(Boom.badRequest), identity) ); - const source = await libs.sources.getSourceConfiguration( - requestContext.core.savedObjects.client, - sourceId - ); + const soClient = (await requestContext.core).savedObjects.client; + const source = await libs.sources.getSourceConfiguration(soClient, sourceId); UsageCollector.countNode(nodeType); diff --git a/x-pack/plugins/infra/server/routes/overview/index.ts b/x-pack/plugins/infra/server/routes/overview/index.ts index fa0e0f4d09af..9736fd0d7c22 100644 --- a/x-pack/plugins/infra/server/routes/overview/index.ts +++ b/x-pack/plugins/infra/server/routes/overview/index.ts @@ -24,10 +24,8 @@ export const initOverviewRoute = (libs: InfraBackendLibs) => { async (requestContext, request, response) => { const options = request.body; const client = createSearchClient(requestContext, framework); - const source = await libs.sources.getSourceConfiguration( - requestContext.core.savedObjects.client, - options.sourceId - ); + const soClient = (await requestContext.core).savedObjects.client; + const source = await libs.sources.getSourceConfiguration(soClient, options.sourceId); const topNResponse = await queryTopNodes(options, client, source); diff --git a/x-pack/plugins/infra/server/routes/snapshot/index.ts b/x-pack/plugins/infra/server/routes/snapshot/index.ts index 06b2104e0e17..99cde598b19e 100644 --- a/x-pack/plugins/infra/server/routes/snapshot/index.ts +++ b/x-pack/plugins/infra/server/routes/snapshot/index.ts @@ -37,10 +37,8 @@ export const initSnapshotRoute = (libs: InfraBackendLibs) => { fold(throwErrors(Boom.badRequest), identity) ); - const source = await libs.sources.getSourceConfiguration( - requestContext.core.savedObjects.client, - snapshotRequest.sourceId - ); + const soClient = (await requestContext.core).savedObjects.client; + const source = await libs.sources.getSourceConfiguration(soClient, snapshotRequest.sourceId); const compositeSize = libs.configuration.inventory.compositeSize; const [, , { logViews }] = await libs.getStartServices(); const logQueryFields: LogQueryFields | undefined = await logViews diff --git a/x-pack/plugins/infra/server/types.ts b/x-pack/plugins/infra/server/types.ts index a791c15d9e6b..e3792c8977cf 100644 --- a/x-pack/plugins/infra/server/types.ts +++ b/x-pack/plugins/infra/server/types.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { CoreSetup, RequestHandlerContext } from '@kbn/core/server'; +import type { CoreSetup, CustomRequestHandlerContext } from '@kbn/core/server'; import type { SearchRequestHandlerContext } from '@kbn/data-plugin/server'; import type { MlPluginSetup } from '@kbn/ml-plugin/server'; import type { InfraStaticSourceConfiguration } from '../common/source_configuration/source_configuration'; @@ -47,7 +47,7 @@ export type InfraRequestHandlerContext = InfraMlRequestHandlerContext & /** * @internal */ -export interface InfraPluginRequestHandlerContext extends RequestHandlerContext { +export type InfraPluginRequestHandlerContext = CustomRequestHandlerContext<{ infra: InfraRequestHandlerContext; search: SearchRequestHandlerContext; -} +}>; diff --git a/x-pack/plugins/infra/server/utils/request_context.ts b/x-pack/plugins/infra/server/utils/request_context.ts index 8924a03549ad..f0546c884399 100644 --- a/x-pack/plugins/infra/server/utils/request_context.ts +++ b/x-pack/plugins/infra/server/utils/request_context.ts @@ -7,7 +7,7 @@ /* eslint-disable max-classes-per-file */ -import { InfraMlRequestHandlerContext, InfraRequestHandlerContext } from '../types'; +import { InfraRequestHandlerContext } from '../types'; export class MissingContextValuesError extends Error { constructor(message?: string) { @@ -23,22 +23,31 @@ export class NoMlPluginError extends Error { } } -export function assertHasInfraPlugins( - context: Context -): asserts context is Context & { infra: Context['infra'] } { +export function assertHasInfraPlugins< + Context extends { infra?: Promise } +>(context: Context): asserts context is Context & { infra: Context['infra'] } { if (context.infra == null) { throw new MissingContextValuesError('Failed to access "infra" context values.'); } } -export function assertHasInfraMlPlugins( +export async function assertHasInfraMlPlugins< + Context extends { infra?: Promise } +>( context: Context -): asserts context is Context & { - infra: Context['infra'] & Required; -} { +): Promise< + Context & { + infra: Promise>; + } +> { assertHasInfraPlugins(context); - if (context.infra?.mlAnomalyDetectors == null || context.infra?.mlSystem == null) { + const infraContext = await context.infra; + if (infraContext?.mlAnomalyDetectors == null || infraContext?.mlSystem == null) { throw new NoMlPluginError('Failed to access ML plugin.'); } + + return context as Context & { + infra: Promise>; + }; } diff --git a/x-pack/plugins/ingest_pipelines/server/routes/api/create.ts b/x-pack/plugins/ingest_pipelines/server/routes/api/create.ts index 90893b2841cc..1e59151f7388 100644 --- a/x-pack/plugins/ingest_pipelines/server/routes/api/create.ts +++ b/x-pack/plugins/ingest_pipelines/server/routes/api/create.ts @@ -30,7 +30,7 @@ export const registerCreateRoute = ({ }, }, async (ctx, req, res) => { - const { client: clusterClient } = ctx.core.elasticsearch; + const { client: clusterClient } = (await ctx.core).elasticsearch; const pipeline = req.body as Pipeline; // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/x-pack/plugins/ingest_pipelines/server/routes/api/delete.ts b/x-pack/plugins/ingest_pipelines/server/routes/api/delete.ts index 1ffa5adabd83..1b2fdd144440 100644 --- a/x-pack/plugins/ingest_pipelines/server/routes/api/delete.ts +++ b/x-pack/plugins/ingest_pipelines/server/routes/api/delete.ts @@ -23,7 +23,7 @@ export const registerDeleteRoute = ({ router }: RouteDependencies): void => { }, }, async (ctx, req, res) => { - const { client: clusterClient } = ctx.core.elasticsearch; + const { client: clusterClient } = (await ctx.core).elasticsearch; const { names } = req.params; const pipelineNames = names.split(','); diff --git a/x-pack/plugins/ingest_pipelines/server/routes/api/documents.ts b/x-pack/plugins/ingest_pipelines/server/routes/api/documents.ts index d9f27ed84f89..b5aff114e44d 100644 --- a/x-pack/plugins/ingest_pipelines/server/routes/api/documents.ts +++ b/x-pack/plugins/ingest_pipelines/server/routes/api/documents.ts @@ -27,7 +27,7 @@ export const registerDocumentsRoute = ({ }, }, async (ctx, req, res) => { - const { client: clusterClient } = ctx.core.elasticsearch; + const { client: clusterClient } = (await ctx.core).elasticsearch; const { index, id } = req.params; try { diff --git a/x-pack/plugins/ingest_pipelines/server/routes/api/get.ts b/x-pack/plugins/ingest_pipelines/server/routes/api/get.ts index 7da2cf3e6a13..c053c80e6571 100644 --- a/x-pack/plugins/ingest_pipelines/server/routes/api/get.ts +++ b/x-pack/plugins/ingest_pipelines/server/routes/api/get.ts @@ -18,7 +18,7 @@ const paramsSchema = schema.object({ export const registerGetRoutes = ({ router, lib: { handleEsError } }: RouteDependencies): void => { // Get all pipelines router.get({ path: API_BASE_PATH, validate: false }, async (ctx, req, res) => { - const { client: clusterClient } = ctx.core.elasticsearch; + const { client: clusterClient } = (await ctx.core).elasticsearch; try { const pipelines = await clusterClient.asCurrentUser.ingest.getPipeline(); @@ -44,7 +44,7 @@ export const registerGetRoutes = ({ router, lib: { handleEsError } }: RouteDepen }, }, async (ctx, req, res) => { - const { client: clusterClient } = ctx.core.elasticsearch; + const { client: clusterClient } = (await ctx.core).elasticsearch; const { name } = req.params; try { diff --git a/x-pack/plugins/ingest_pipelines/server/routes/api/privileges.ts b/x-pack/plugins/ingest_pipelines/server/routes/api/privileges.ts index 114a5b884033..175ef4eb3717 100644 --- a/x-pack/plugins/ingest_pipelines/server/routes/api/privileges.ts +++ b/x-pack/plugins/ingest_pipelines/server/routes/api/privileges.ts @@ -36,7 +36,7 @@ export const registerPrivilegesRoute = ({ router, config }: RouteDependencies) = return res.ok({ body: privilegesResult }); } - const { client: clusterClient } = ctx.core.elasticsearch; + const { client: clusterClient } = (await ctx.core).elasticsearch; const { has_all_requested: hasAllPrivileges, cluster } = await clusterClient.asCurrentUser.security.hasPrivileges({ diff --git a/x-pack/plugins/ingest_pipelines/server/routes/api/simulate.ts b/x-pack/plugins/ingest_pipelines/server/routes/api/simulate.ts index 9e4e894e2c7c..a313afbc2a2c 100644 --- a/x-pack/plugins/ingest_pipelines/server/routes/api/simulate.ts +++ b/x-pack/plugins/ingest_pipelines/server/routes/api/simulate.ts @@ -29,7 +29,7 @@ export const registerSimulateRoute = ({ }, }, async (ctx, req, res) => { - const { client: clusterClient } = ctx.core.elasticsearch; + const { client: clusterClient } = (await ctx.core).elasticsearch; const { pipeline, documents, verbose } = req.body; diff --git a/x-pack/plugins/ingest_pipelines/server/routes/api/update.ts b/x-pack/plugins/ingest_pipelines/server/routes/api/update.ts index 51983f12e6a6..5b1b032c8aed 100644 --- a/x-pack/plugins/ingest_pipelines/server/routes/api/update.ts +++ b/x-pack/plugins/ingest_pipelines/server/routes/api/update.ts @@ -30,7 +30,7 @@ export const registerUpdateRoute = ({ }, }, async (ctx, req, res) => { - const { client: clusterClient } = ctx.core.elasticsearch; + const { client: clusterClient } = (await ctx.core).elasticsearch; const { name } = req.params; // eslint-disable-next-line @typescript-eslint/naming-convention const { description, processors, version, on_failure } = req.body; diff --git a/x-pack/plugins/lens/kibana.json b/x-pack/plugins/lens/kibana.json index 237a25ce78a9..8ed33fb30452 100644 --- a/x-pack/plugins/lens/kibana.json +++ b/x-pack/plugins/lens/kibana.json @@ -22,7 +22,8 @@ "dataViewFieldEditor", "expressionGauge", "expressionHeatmap", - "eventAnnotation" + "eventAnnotation", + "unifiedSearch" ], "optionalPlugins": [ "expressionXY", diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx index a9e37e2d53d7..2c03ad902ce8 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx @@ -93,6 +93,7 @@ export function DimensionEditor(props: DimensionEditorProps) { savedObjectsClient: props.savedObjectsClient, http: props.http, storage: props.storage, + unifiedSearch: props.unifiedSearch, }; const { fieldByOperation, operationWithoutField } = operationSupportMatrix; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx index f4d27a16f19f..3ab363372567 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx @@ -16,6 +16,7 @@ import { EuiSelect, EuiButtonIcon, } from '@elastic/eui'; +import { unifiedSearchPluginMock } from '@kbn/unified-search-plugin/public/mocks'; import { DataPublicPluginStart } from '@kbn/data-plugin/public'; import { IndexPatternDimensionEditorComponent, @@ -208,6 +209,7 @@ describe('IndexPatternDimensionEditorPanel', () => { uiSettings: {} as IUiSettingsClient, savedObjectsClient: {} as SavedObjectsClientContract, http: {} as HttpSetup, + unifiedSearch: unifiedSearchPluginMock.createStartContract(), data: { fieldFormats: { getType: jest.fn().mockReturnValue({ diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.tsx index cdccdb65e70d..4f20db3004e8 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.tsx @@ -9,6 +9,7 @@ import React, { memo, useMemo } from 'react'; import { IUiSettingsClient, SavedObjectsClientContract, HttpSetup } from '@kbn/core/public'; import { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; import { DataPublicPluginStart } from '@kbn/data-plugin/public'; +import { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; import { DatasourceDimensionTriggerProps, DatasourceDimensionEditorProps } from '../../types'; import { GenericIndexPatternColumn } from '../indexpattern'; import { isColumnInvalid } from '../utils'; @@ -31,6 +32,7 @@ export type IndexPatternDimensionEditorProps = layerId: string; http: HttpSetup; data: DataPublicPluginStart; + unifiedSearch: UnifiedSearchPublicPluginStart; uniqueLabel: string; dateRange: DateRange; }; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/droppable/droppable.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/droppable/droppable.test.ts index c5da32f3a7ba..412f8211844b 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/droppable/droppable.test.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/droppable/droppable.test.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; import { DataPublicPluginStart } from '@kbn/data-plugin/public'; import { IndexPatternDimensionEditorProps } from '../dimension_panel'; import { onDrop } from './on_drop_handler'; @@ -322,6 +323,7 @@ describe('IndexPatternDimensionEditorPanel', () => { }), } as unknown as DataPublicPluginStart['fieldFormats'], } as unknown as DataPublicPluginStart, + unifiedSearch: {} as UnifiedSearchPublicPluginStart, core: {} as CoreSetup, dimensionGroups: [], isFullscreen: false, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/reference_editor.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/reference_editor.test.tsx index 1cfea4b6c32e..804cbde3d170 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/reference_editor.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/reference_editor.test.tsx @@ -10,6 +10,7 @@ import { ReactWrapper, ShallowWrapper } from 'enzyme'; import { act } from 'react-dom/test-utils'; import { EuiComboBox } from '@elastic/eui'; import { mountWithIntl as mount } from '@kbn/test-jest-helpers'; +import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; import type { IUiSettingsClient, SavedObjectsClientContract, HttpSetup } from '@kbn/core/public'; import { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; @@ -54,6 +55,7 @@ describe('reference editor', () => { savedObjectsClient: {} as SavedObjectsClientContract, http: {} as HttpSetup, data: {} as DataPublicPluginStart, + unifiedSearch: {} as UnifiedSearchPublicPluginStart, dimensionGroups: [], isFullscreen: false, toggleFullscreen: jest.fn(), diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/reference_editor.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/reference_editor.tsx index 4e373859d625..3c16d271401a 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/reference_editor.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/reference_editor.tsx @@ -15,6 +15,7 @@ import { EuiComboBox, EuiComboBoxOptionOption, } from '@elastic/eui'; +import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; import type { IUiSettingsClient, SavedObjectsClientContract, HttpSetup } from '@kbn/core/public'; import { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; @@ -65,6 +66,7 @@ export interface ReferenceEditorProps { savedObjectsClient: SavedObjectsClientContract; http: HttpSetup; data: DataPublicPluginStart; + unifiedSearch: UnifiedSearchPublicPluginStart; paramEditorCustomProps?: ParamEditorCustomProps; } diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/index.ts b/x-pack/plugins/lens/public/indexpattern_datasource/index.ts index d5ac84568ff1..41f2a4df9bcd 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/index.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/index.ts @@ -7,11 +7,12 @@ import type { CoreSetup } from '@kbn/core/public'; import { createStartServicesGetter, Storage } from '@kbn/kibana-utils-plugin/public'; +import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; +import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; import type { ExpressionsSetup } from '@kbn/expressions-plugin/public'; import type { ChartsPluginSetup } from '@kbn/charts-plugin/public'; import type { IndexPatternFieldEditorStart } from '@kbn/data-view-field-editor-plugin/public'; import type { DataPublicPluginSetup, DataPublicPluginStart } from '@kbn/data-plugin/public'; -import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; import type { UiActionsStart } from '@kbn/ui-actions-plugin/public'; import type { FieldFormatsStart, FieldFormatsSetup } from '@kbn/field-formats-plugin/public'; import type { EditorFrameSetup } from '../types'; @@ -28,6 +29,7 @@ export interface IndexPatternDatasourceSetupPlugins { export interface IndexPatternDatasourceStartPlugins { data: DataPublicPluginStart; + unifiedSearch: UnifiedSearchPublicPluginStart; fieldFormats: FieldFormatsStart; dataViewFieldEditor: IndexPatternFieldEditorStart; dataViews: DataViewsPublicPluginStart; @@ -58,14 +60,17 @@ export class IndexPatternDatasource { fieldFormatsSetup.register([suffixFormatter]); } - const [coreStart, { dataViewFieldEditor, uiActions, data, fieldFormats, dataViews }] = - await core.getStartServices(); + const [ + coreStart, + { dataViewFieldEditor, uiActions, data, fieldFormats, dataViews, unifiedSearch }, + ] = await core.getStartServices(); return getIndexPatternDatasource({ core: coreStart, fieldFormats, storage: new Storage(localStorage), data, + unifiedSearch, dataViews, charts, dataViewFieldEditor, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.test.ts index 6ae1f0b6f7f4..db10c420b90d 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.test.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.test.ts @@ -6,16 +6,22 @@ */ import React, { ReactElement } from 'react'; +import { SavedObjectReference } from '@kbn/core/public'; import { isFragment } from 'react-is'; -import { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; -import { getIndexPatternDatasource, GenericIndexPatternColumn } from './indexpattern'; -import { DatasourcePublicAPI, Datasource, FramePublicAPI, OperationDescriptor } from '../types'; import { coreMock } from '@kbn/core/public/mocks'; +import { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; import { IndexPatternPersistedState, IndexPatternPrivateState } from './types'; +import { unifiedSearchPluginMock } from '@kbn/unified-search-plugin/public/mocks'; import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; import { dataViewPluginMocks } from '@kbn/data-views-plugin/public/mocks'; import { Ast } from '@kbn/interpreter'; import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; +import { indexPatternFieldEditorPluginMock } from '@kbn/data-view-field-editor-plugin/public/mocks'; +import { uiActionsPluginMock } from '@kbn/ui-actions-plugin/public/mocks'; +import { fieldFormatsServiceMock } from '@kbn/field-formats-plugin/public/mocks'; +import { TinymathAST } from '@kbn/tinymath'; +import { getIndexPatternDatasource, GenericIndexPatternColumn } from './indexpattern'; +import { DatasourcePublicAPI, Datasource, FramePublicAPI, OperationDescriptor } from '../types'; import { getFieldByNameFactory } from './pure_helpers'; import { operationDefinitionMap, @@ -29,11 +35,6 @@ import { FiltersIndexPatternColumn, } from './operations'; import { createMockedFullReference } from './operations/mocks'; -import { indexPatternFieldEditorPluginMock } from '@kbn/data-view-field-editor-plugin/public/mocks'; -import { uiActionsPluginMock } from '@kbn/ui-actions-plugin/public/mocks'; -import { fieldFormatsServiceMock } from '@kbn/field-formats-plugin/public/mocks'; -import { TinymathAST } from '@kbn/tinymath'; -import { SavedObjectReference } from '@kbn/core/public'; import { cloneDeep } from 'lodash'; import { DatatableColumn } from '@kbn/expressions-plugin'; @@ -186,6 +187,7 @@ describe('IndexPattern Data Source', () => { beforeEach(() => { indexPatternDatasource = getIndexPatternDatasource({ + unifiedSearch: unifiedSearchPluginMock.createStartContract(), storage: {} as IStorageWrapper, core: coreMock.createStart(), data: dataPluginMock.createStartContract(), diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx index b5fc2d012c2e..b72519c2191b 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx @@ -20,6 +20,7 @@ import { DataPublicPluginStart, ES_FIELD_TYPES } from '@kbn/data-plugin/public'; import { VisualizeFieldContext } from '@kbn/ui-actions-plugin/public'; import { ChartsPluginSetup } from '@kbn/charts-plugin/public'; import { UiActionsStart } from '@kbn/ui-actions-plugin/public'; +import { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; import type { DatasourceDimensionEditorProps, DatasourceDimensionTriggerProps, @@ -115,6 +116,7 @@ export function getIndexPatternDatasource({ core, storage, data, + unifiedSearch, dataViews, fieldFormats, charts, @@ -124,6 +126,7 @@ export function getIndexPatternDatasource({ core: CoreStart; storage: IStorageWrapper; data: DataPublicPluginStart; + unifiedSearch: UnifiedSearchPublicPluginStart; dataViews: DataViewsPublicPluginStart; fieldFormats: FieldFormatsStart; charts: ChartsPluginSetup; @@ -370,6 +373,7 @@ export function getIndexPatternDatasource({ savedObjectsClient={core.savedObjects.client} http={core.http} data={data} + unifiedSearch={unifiedSearch} uniqueLabel={columnLabelMap[props.columnId]} {...props} /> diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.test.tsx index cc1303a2a4f6..d4491fbba00c 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.test.tsx @@ -10,6 +10,7 @@ import type { DateHistogramIndexPatternColumn } from './date_histogram'; import { dateHistogramOperation } from '.'; import { shallow } from 'enzyme'; import { EuiSwitch } from '@elastic/eui'; +import { unifiedSearchPluginMock } from '@kbn/unified-search-plugin/public/mocks'; import type { IUiSettingsClient, SavedObjectsClientContract, HttpSetup } from '@kbn/core/public'; import type { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; import { UI_SETTINGS } from '@kbn/data-plugin/public'; @@ -19,6 +20,7 @@ import type { IndexPatternLayer, IndexPattern } from '../../types'; import { getFieldByNameFactory } from '../../pure_helpers'; const dataStart = dataPluginMock.createStartContract(); +const unifiedSearchStart = unifiedSearchPluginMock.createStartContract(); dataStart.search.aggs.calculateAutoTimeExpression = getCalculateAutoTimeExpression( (path: string) => { if (path === UI_SETTINGS.HISTOGRAM_MAX_BARS) { @@ -93,6 +95,7 @@ const defaultOptions = { toDate: 'now', }, data: dataStart, + unifiedSearch: unifiedSearchStart, http: {} as HttpSetup, indexPattern: indexPattern1, operationDefinitionMap: {}, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filters.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filters.test.tsx index 0e35a7e96cd7..7208965ec080 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filters.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filters.test.tsx @@ -8,6 +8,7 @@ import React from 'react'; import { mount } from 'enzyme'; import { act } from 'react-dom/test-utils'; +import { unifiedSearchPluginMock } from '@kbn/unified-search-plugin/public/mocks'; import type { IUiSettingsClient, SavedObjectsClientContract, HttpSetup } from '@kbn/core/public'; import type { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; @@ -25,6 +26,7 @@ const defaultProps = { savedObjectsClient: {} as SavedObjectsClientContract, dateRange: { fromDate: 'now-1d', toDate: 'now' }, data: dataPluginMock.createStartContract(), + unifiedSearch: unifiedSearchPluginMock.createStartContract(), http: {} as HttpSetup, indexPattern: createMockedIndexPattern(), operationDefinitionMap: {}, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/editor/formula_editor.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/editor/formula_editor.tsx index fe579e2e5a5f..ecc46babcfe7 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/editor/formula_editor.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/editor/formula_editor.tsx @@ -89,7 +89,7 @@ export function FormulaEditor({ columnId, indexPattern, operationDefinitionMap, - data, + unifiedSearch, toggleFullscreen, isFullscreen, setIsCloseable, @@ -416,7 +416,7 @@ export function FormulaEditor({ context, indexPattern, operationDefinitionMap: visibleOperationsMap, - data, + unifiedSearch, dateHistogramInterval: baseIntervalRef.current, }); } @@ -427,7 +427,7 @@ export function FormulaEditor({ context, indexPattern, operationDefinitionMap: visibleOperationsMap, - data, + unifiedSearch, dateHistogramInterval: baseIntervalRef.current, }); } @@ -444,7 +444,7 @@ export function FormulaEditor({ ), }; }, - [indexPattern, visibleOperationsMap, data, baseIntervalRef] + [indexPattern, visibleOperationsMap, unifiedSearch, baseIntervalRef] ); const provideSignatureHelp = useCallback( diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/editor/math_completion.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/editor/math_completion.test.ts index 92014b340e41..0039f486933b 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/editor/math_completion.test.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/editor/math_completion.test.ts @@ -7,11 +7,11 @@ import { parse } from '@kbn/tinymath'; import { monaco } from '@kbn/monaco'; +import { unifiedSearchPluginMock } from '@kbn/unified-search-plugin/public/mocks'; import { createMockedIndexPattern } from '../../../../mocks'; import { GenericOperationDefinition } from '../..'; import type { IndexPatternField } from '../../../../types'; import type { OperationMetadata } from '../../../../../types'; -import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; import { tinymathFunctions } from '../util'; import { getSignatureHelp, @@ -217,7 +217,7 @@ describe('math completion', () => { }, indexPattern: createMockedIndexPattern(), operationDefinitionMap, - data: dataPluginMock.createStartContract(), + unifiedSearch: unifiedSearchPluginMock.createStartContract(), }); expect(results.list).toHaveLength(4 + Object.keys(tinymathFunctions).length); ['sum', 'moving_average', 'cumulative_sum', 'last_value'].forEach((key) => { @@ -238,7 +238,7 @@ describe('math completion', () => { }, indexPattern: createMockedIndexPattern(), operationDefinitionMap, - data: dataPluginMock.createStartContract(), + unifiedSearch: unifiedSearchPluginMock.createStartContract(), }); expect(results.list).toHaveLength(2); ['sum', 'last_value'].forEach((key) => { @@ -256,7 +256,7 @@ describe('math completion', () => { }, indexPattern: createMockedIndexPattern(), operationDefinitionMap, - data: dataPluginMock.createStartContract(), + unifiedSearch: unifiedSearchPluginMock.createStartContract(), }); expect(results.list).toEqual(['window']); }); @@ -271,7 +271,7 @@ describe('math completion', () => { }, indexPattern: createMockedIndexPattern(), operationDefinitionMap, - data: dataPluginMock.createStartContract(), + unifiedSearch: unifiedSearchPluginMock.createStartContract(), }); expect(results.list).toEqual([]); }); @@ -286,7 +286,7 @@ describe('math completion', () => { }, indexPattern: createMockedIndexPattern(), operationDefinitionMap, - data: dataPluginMock.createStartContract(), + unifiedSearch: unifiedSearchPluginMock.createStartContract(), }); expect(results.list).toHaveLength(4 + Object.keys(tinymathFunctions).length); ['sum', 'moving_average', 'cumulative_sum', 'last_value'].forEach((key) => { @@ -307,7 +307,7 @@ describe('math completion', () => { }, indexPattern: createMockedIndexPattern(), operationDefinitionMap, - data: dataPluginMock.createStartContract(), + unifiedSearch: unifiedSearchPluginMock.createStartContract(), }); expect(results.list).toHaveLength(4 + Object.keys(tinymathFunctions).length); ['sum', 'moving_average', 'cumulative_sum', 'last_value'].forEach((key) => { @@ -328,7 +328,7 @@ describe('math completion', () => { }, indexPattern: createMockedIndexPattern(), operationDefinitionMap, - data: dataPluginMock.createStartContract(), + unifiedSearch: unifiedSearchPluginMock.createStartContract(), }); expect(results.list).toHaveLength(0); }); @@ -343,7 +343,7 @@ describe('math completion', () => { }, indexPattern: createMockedIndexPattern(), operationDefinitionMap, - data: dataPluginMock.createStartContract(), + unifiedSearch: unifiedSearchPluginMock.createStartContract(), }); expect(results.list).toEqual(['bytes', 'memory']); }); @@ -358,7 +358,7 @@ describe('math completion', () => { }, indexPattern: createMockedIndexPattern(), operationDefinitionMap, - data: dataPluginMock.createStartContract(), + unifiedSearch: unifiedSearchPluginMock.createStartContract(), }); expect(results.list).toEqual(['bytes', 'memory']); }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/editor/math_completion.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/editor/math_completion.ts index 33dc7a343be4..a1b629be9c13 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/editor/math_completion.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/editor/math_completion.ts @@ -16,7 +16,10 @@ import { TinymathVariable, TinymathNamedArgument, } from '@kbn/tinymath'; -import type { DataPublicPluginStart, QuerySuggestion } from '@kbn/data-plugin/public'; +import type { + UnifiedSearchPublicPluginStart, + QuerySuggestion, +} from '@kbn/unified-search-plugin/public'; import { parseTimeShift } from '@kbn/data-plugin/common'; import { IndexPattern } from '../../../../types'; import { memoizedGetAvailableOperationsByMetadata } from '../../../operations'; @@ -117,7 +120,7 @@ export async function suggest({ context, indexPattern, operationDefinitionMap, - data, + unifiedSearch, dateHistogramInterval, }: { expression: string; @@ -125,7 +128,7 @@ export async function suggest({ context: monaco.languages.CompletionContext; indexPattern: IndexPattern; operationDefinitionMap: Record; - data: DataPublicPluginStart; + unifiedSearch: UnifiedSearchPublicPluginStart; dateHistogramInterval?: number; }): Promise { const text = @@ -145,7 +148,7 @@ export async function suggest({ if (tokenInfo?.parent && (context.triggerCharacter === '=' || isNamedArgument)) { return await getNamedArgumentSuggestions({ ast: tokenAst as TinymathNamedArgument, - data, + unifiedSearch, indexPattern, dateHistogramInterval, }); @@ -328,13 +331,13 @@ function getArgumentSuggestions( export async function getNamedArgumentSuggestions({ ast, - data, + unifiedSearch, indexPattern, dateHistogramInterval, }: { ast: TinymathNamedArgument; indexPattern: IndexPattern; - data: DataPublicPluginStart; + unifiedSearch: UnifiedSearchPublicPluginStart; dateHistogramInterval?: number; }) { if (ast.name === 'shift') { @@ -356,14 +359,14 @@ export async function getNamedArgumentSuggestions({ if (ast.name !== 'kql' && ast.name !== 'lucene') { return { list: [], type: SUGGESTION_TYPE.KQL }; } - if (!data.autocomplete.hasQuerySuggestions(ast.name === 'kql' ? 'kuery' : 'lucene')) { + if (!unifiedSearch.autocomplete.hasQuerySuggestions(ast.name === 'kql' ? 'kuery' : 'lucene')) { return { list: [], type: SUGGESTION_TYPE.KQL }; } const query = ast.value.split(MARKER)[0]; const position = ast.value.indexOf(MARKER) + 1; - const suggestions = await data.autocomplete.getQuerySuggestions({ + const suggestions = await unifiedSearch.autocomplete.getQuerySuggestions({ language: ast.name === 'kql' ? 'kuery' : 'lucene', query, selectionStart: position, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/index.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/index.ts index 9ac5d683b74a..e62bab6bd808 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/index.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/index.ts @@ -14,6 +14,7 @@ import { import { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; import { ExpressionAstFunction } from '@kbn/expressions-plugin/public'; import { DataPublicPluginStart } from '@kbn/data-plugin/public'; +import { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; import { termsOperation } from './terms'; import { filtersOperation } from './filters'; import { cardinalityOperation } from './cardinality'; @@ -163,6 +164,7 @@ export interface ParamEditorProps { http: HttpSetup; dateRange: DateRange; data: DataPublicPluginStart; + unifiedSearch: UnifiedSearchPublicPluginStart; activeData?: IndexPatternDimensionEditorProps['activeData']; operationDefinitionMap: Record; paramEditorCustomProps?: ParamEditorCustomProps; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.test.tsx index 556c91d3fa46..5e3f1f004366 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.test.tsx @@ -8,6 +8,7 @@ import React from 'react'; import { shallow, ShallowWrapper } from 'enzyme'; import { EuiComboBox, EuiFormRow } from '@elastic/eui'; +import { unifiedSearchPluginMock } from '@kbn/unified-search-plugin/public/mocks'; import { IUiSettingsClient, SavedObjectsClientContract, HttpSetup } from '@kbn/core/public'; import { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; @@ -25,6 +26,7 @@ const defaultProps = { uiSettings: uiSettingsMock, savedObjectsClient: {} as SavedObjectsClientContract, dateRange: { fromDate: 'now-1d', toDate: 'now' }, + unifiedSearch: unifiedSearchPluginMock.createStartContract(), data: dataPluginMock.createStartContract(), http: {} as HttpSetup, indexPattern: { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/percentile.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/percentile.test.tsx index a900e4cc29ae..831bb03c89ab 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/percentile.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/percentile.test.tsx @@ -6,17 +6,18 @@ */ import React, { ChangeEvent } from 'react'; -import { shallow, mount } from 'enzyme'; +import { act } from 'react-dom/test-utils'; +import { EuiRange } from '@elastic/eui'; import { IUiSettingsClient, SavedObjectsClientContract, HttpSetup } from '@kbn/core/public'; +import { EuiFormRow } from '@elastic/eui'; +import { shallow, mount } from 'enzyme'; +import { unifiedSearchPluginMock } from '@kbn/unified-search-plugin/public/mocks'; import { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; import { createMockedIndexPattern } from '../../mocks'; import { percentileOperation } from '.'; import { IndexPattern, IndexPatternLayer } from '../../types'; import { PercentileIndexPatternColumn } from './percentile'; -import { EuiRange } from '@elastic/eui'; -import { act } from 'react-dom/test-utils'; -import { EuiFormRow } from '@elastic/eui'; import { TermsIndexPatternColumn } from './terms'; jest.mock('lodash', () => { @@ -36,6 +37,7 @@ const defaultProps = { savedObjectsClient: {} as SavedObjectsClientContract, dateRange: { fromDate: 'now-1d', toDate: 'now' }, data: dataPluginMock.createStartContract(), + unifiedSearch: unifiedSearchPluginMock.createStartContract(), http: {} as HttpSetup, indexPattern: { ...createMockedIndexPattern(), diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.test.tsx index 534fcdbaf2d0..5f882a3ec211 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.test.tsx @@ -11,8 +11,9 @@ import { act } from 'react-dom/test-utils'; import { EuiFieldNumber, EuiRange, EuiButtonEmpty, EuiLink, EuiText } from '@elastic/eui'; import { IUiSettingsClient, SavedObjectsClientContract, HttpSetup } from '@kbn/core/public'; import { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; -import type { IndexPatternLayer, IndexPattern } from '../../../types'; import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; +import { unifiedSearchPluginMock } from '@kbn/unified-search-plugin/public/mocks'; +import type { IndexPatternLayer, IndexPattern } from '../../../types'; import { rangeOperation } from '..'; import { RangeIndexPatternColumn } from './ranges'; import { @@ -51,6 +52,7 @@ jest.mock('lodash', () => { }); const dataPluginMockValue = dataPluginMock.createStartContract(); +const unifiedSearchPluginMockValue = unifiedSearchPluginMock.createStartContract(); // need to overwrite the formatter field first dataPluginMockValue.fieldFormats.deserialize = jest.fn().mockImplementation(({ id, params }) => { return { @@ -84,6 +86,7 @@ const defaultOptions = { toDate: 'now', }, data: dataPluginMockValue, + unifiedSearch: unifiedSearchPluginMockValue, http: {} as HttpSetup, indexPattern: { id: '1', diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/static_value.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/static_value.test.tsx index 546e25308346..60a871efd85c 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/static_value.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/static_value.test.tsx @@ -6,16 +6,17 @@ */ import React from 'react'; -import { shallow, mount } from 'enzyme'; +import { act } from 'react-dom/test-utils'; +import { EuiFieldNumber } from '@elastic/eui'; import { IUiSettingsClient, SavedObjectsClientContract, HttpSetup } from '@kbn/core/public'; import { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; +import { shallow, mount } from 'enzyme'; +import { unifiedSearchPluginMock } from '@kbn/unified-search-plugin/public/mocks'; import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; import { createMockedIndexPattern } from '../../mocks'; import { staticValueOperation } from '.'; import { IndexPattern, IndexPatternLayer } from '../../types'; import { StaticValueIndexPatternColumn } from './static_value'; -import { EuiFieldNumber } from '@elastic/eui'; -import { act } from 'react-dom/test-utils'; import { TermsIndexPatternColumn } from './terms'; jest.mock('lodash', () => { @@ -35,6 +36,7 @@ const defaultProps = { savedObjectsClient: {} as SavedObjectsClientContract, dateRange: { fromDate: 'now-1d', toDate: 'now' }, data: dataPluginMock.createStartContract(), + unifiedSearch: unifiedSearchPluginMock.createStartContract(), http: {} as HttpSetup, indexPattern: { ...createMockedIndexPattern(), diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/terms.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/terms.test.tsx index e028d81ad454..8cfeb1b68e5b 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/terms.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/terms.test.tsx @@ -15,6 +15,7 @@ import type { HttpSetup, CoreStart, } from '@kbn/core/public'; +import { unifiedSearchPluginMock } from '@kbn/unified-search-plugin/public/mocks'; import type { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; import { createMockedIndexPattern } from '../../../mocks'; @@ -58,6 +59,7 @@ const defaultProps = { savedObjectsClient: {} as SavedObjectsClientContract, dateRange: { fromDate: 'now-1d', toDate: 'now' }, data: dataPluginMock.createStartContract(), + unifiedSearch: unifiedSearchPluginMock.createStartContract(), http: {} as HttpSetup, indexPattern: createMockedIndexPattern(), // need to provide the terms operation as some helpers use operation specific features diff --git a/x-pack/plugins/lens/public/plugin.ts b/x-pack/plugins/lens/public/plugin.ts index f2d82e96b98c..b39c14cd8245 100644 --- a/x-pack/plugins/lens/public/plugin.ts +++ b/x-pack/plugins/lens/public/plugin.ts @@ -13,11 +13,8 @@ import type { UsageCollectionStart, } from '@kbn/usage-collection-plugin/public'; import type { DataPublicPluginSetup, DataPublicPluginStart } from '@kbn/data-plugin/public'; -import { - CONTEXT_MENU_TRIGGER, - EmbeddableSetup, - EmbeddableStart, -} from '@kbn/embeddable-plugin/public'; +import type { EmbeddableSetup, EmbeddableStart } from '@kbn/embeddable-plugin/public'; +import { CONTEXT_MENU_TRIGGER } from '@kbn/embeddable-plugin/public'; import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; import type { DashboardStart } from '@kbn/dashboard-plugin/public'; import type { SpacesPluginStart } from '@kbn/spaces-plugin/public'; @@ -45,6 +42,7 @@ import { import { VISUALIZE_EDITOR_TRIGGER } from '@kbn/visualizations-plugin/public'; import { createStartServicesGetter } from '@kbn/kibana-utils-plugin/public'; import type { DiscoverSetup, DiscoverStart } from '@kbn/discover-plugin/public'; +import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; import type { EditorFrameService as EditorFrameServiceType } from './editor_frame_service'; import type { IndexPatternDatasource as IndexPatternDatasourceType, @@ -111,6 +109,7 @@ export interface LensPluginSetupDependencies { export interface LensPluginStartDependencies { data: DataPublicPluginStart; + unifiedSearch: UnifiedSearchPublicPluginStart; dataViews: DataViewsPublicPluginStart; fieldFormats: FieldFormatsStart; expressions: ExpressionsStart; diff --git a/x-pack/plugins/lens/server/routes/existing_fields.ts b/x-pack/plugins/lens/server/routes/existing_fields.ts index 542cdb33df8a..61df3bb45891 100644 --- a/x-pack/plugins/lens/server/routes/existing_fields.ts +++ b/x-pack/plugins/lens/server/routes/existing_fields.ts @@ -136,7 +136,8 @@ async function fetchFieldExistence({ }); } - const metaFields: string[] = await context.core.uiSettings.client.get(UI_SETTINGS.META_FIELDS); + const uiSettingsClient = (await context.core).uiSettings.client; + const metaFields: string[] = await uiSettingsClient.get(UI_SETTINGS.META_FIELDS); const dataView = await dataViewsService.get(indexPatternId); const allFields = buildFieldList(dataView, metaFields); const existingFieldList = await dataViewsService.getFieldsForIndexPattern(dataView, { @@ -169,7 +170,8 @@ async function legacyFetchFieldExistenceSampling({ timeFieldName?: string; includeFrozen: boolean; }) { - const metaFields: string[] = await context.core.uiSettings.client.get(UI_SETTINGS.META_FIELDS); + const coreContext = await context.core; + const metaFields: string[] = await coreContext.uiSettings.client.get(UI_SETTINGS.META_FIELDS); const indexPattern = await dataViewsService.get(indexPatternId); const fields = buildFieldList(indexPattern, metaFields); @@ -179,7 +181,7 @@ async function legacyFetchFieldExistenceSampling({ fromDate, toDate, dslQuery, - client: context.core.elasticsearch.client.asCurrentUser, + client: coreContext.elasticsearch.client.asCurrentUser, index: indexPattern.title, timeFieldName: timeFieldName || indexPattern.timeFieldName, fields, diff --git a/x-pack/plugins/lens/server/routes/field_stats.ts b/x-pack/plugins/lens/server/routes/field_stats.ts index 1da732ab60ab..35a15ea44be6 100644 --- a/x-pack/plugins/lens/server/routes/field_stats.ts +++ b/x-pack/plugins/lens/server/routes/field_stats.ts @@ -39,7 +39,7 @@ export async function initFieldsRoute(setup: CoreSetup) { }, }, async (context, req, res) => { - const requestClient = context.core.elasticsearch.client.asCurrentUser; + const requestClient = (await context.core).elasticsearch.client.asCurrentUser; const { fromDate, toDate, fieldName, dslQuery, size } = req.body; const [{ savedObjects, elasticsearch }, { dataViews }] = await setup.getStartServices(); diff --git a/x-pack/plugins/lens/server/routes/telemetry.ts b/x-pack/plugins/lens/server/routes/telemetry.ts index 8ef45a5029e9..01c843cf5489 100644 --- a/x-pack/plugins/lens/server/routes/telemetry.ts +++ b/x-pack/plugins/lens/server/routes/telemetry.ts @@ -33,7 +33,7 @@ export async function initLensUsageRoute(setup: CoreSetup) const { events, suggestionEvents } = req.body; try { - const client = context.core.savedObjects.client; + const client = (await context.core).savedObjects.client; const allEvents: Array<{ type: 'lens-ui-telemetry'; diff --git a/x-pack/plugins/license_management/server/routes/api/license/register_license_route.ts b/x-pack/plugins/license_management/server/routes/api/license/register_license_route.ts index 03e57b1a12cd..0a9bbacaff4d 100644 --- a/x-pack/plugins/license_management/server/routes/api/license/register_license_route.ts +++ b/x-pack/plugins/license_management/server/routes/api/license/register_license_route.ts @@ -26,7 +26,7 @@ export function registerLicenseRoute({ }, }, async (ctx, req, res) => { - const { client } = ctx.core.elasticsearch; + const { client } = (await ctx.core).elasticsearch; try { return res.ok({ body: await putLicense({ diff --git a/x-pack/plugins/license_management/server/routes/api/license/register_permissions_route.ts b/x-pack/plugins/license_management/server/routes/api/license/register_permissions_route.ts index 01aae5cd6d44..837227372691 100644 --- a/x-pack/plugins/license_management/server/routes/api/license/register_permissions_route.ts +++ b/x-pack/plugins/license_management/server/routes/api/license/register_permissions_route.ts @@ -15,7 +15,7 @@ export function registerPermissionsRoute({ config: { isSecurityEnabled }, }: RouteDependencies) { router.post({ path: addBasePath('/permissions'), validate: false }, async (ctx, req, res) => { - const { client } = ctx.core.elasticsearch; + const { client } = (await ctx.core).elasticsearch; try { return res.ok({ diff --git a/x-pack/plugins/license_management/server/routes/api/license/register_start_basic_route.ts b/x-pack/plugins/license_management/server/routes/api/license/register_start_basic_route.ts index 60e72d297b2e..fa9f13ade07a 100644 --- a/x-pack/plugins/license_management/server/routes/api/license/register_start_basic_route.ts +++ b/x-pack/plugins/license_management/server/routes/api/license/register_start_basic_route.ts @@ -21,7 +21,7 @@ export function registerStartBasicRoute({ validate: { query: schema.object({ acknowledge: schema.string() }) }, }, async (ctx, req, res) => { - const { client } = ctx.core.elasticsearch; + const { client } = (await ctx.core).elasticsearch; try { return res.ok({ body: await startBasic({ diff --git a/x-pack/plugins/license_management/server/routes/api/license/register_start_trial_routes.ts b/x-pack/plugins/license_management/server/routes/api/license/register_start_trial_routes.ts index 43ab7c5eafdb..5ff25f4d2130 100644 --- a/x-pack/plugins/license_management/server/routes/api/license/register_start_trial_routes.ts +++ b/x-pack/plugins/license_management/server/routes/api/license/register_start_trial_routes.ts @@ -15,7 +15,7 @@ export function registerStartTrialRoutes({ plugins: { licensing }, }: RouteDependencies) { router.get({ path: addBasePath('/start_trial'), validate: false }, async (ctx, req, res) => { - const { client } = ctx.core.elasticsearch; + const { client } = (await ctx.core).elasticsearch; try { return res.ok({ body: await canStartTrial(client) }); } catch (error) { @@ -24,7 +24,7 @@ export function registerStartTrialRoutes({ }); router.post({ path: addBasePath('/start_trial'), validate: false }, async (ctx, req, res) => { - const { client } = ctx.core.elasticsearch; + const { client } = (await ctx.core).elasticsearch; try { return res.ok({ body: await startTrial({ client, licensing }), diff --git a/x-pack/plugins/licensing/server/routes/info.ts b/x-pack/plugins/licensing/server/routes/info.ts index 9db7a1c8ca1b..1cbc7b639896 100644 --- a/x-pack/plugins/licensing/server/routes/info.ts +++ b/x-pack/plugins/licensing/server/routes/info.ts @@ -8,9 +8,12 @@ import { LicensingRouter } from '../types'; export function registerInfoRoute(router: LicensingRouter) { - router.get({ path: '/api/licensing/info', validate: false }, (context, request, response) => { - return response.ok({ - body: context.licensing.license, - }); - }); + router.get( + { path: '/api/licensing/info', validate: false }, + async (context, request, response) => { + return response.ok({ + body: (await context.licensing).license, + }); + } + ); } diff --git a/x-pack/plugins/licensing/server/routes/internal/notify_feature_usage.ts b/x-pack/plugins/licensing/server/routes/internal/notify_feature_usage.ts index 2cc4a64b63a3..a33cf4284245 100644 --- a/x-pack/plugins/licensing/server/routes/internal/notify_feature_usage.ts +++ b/x-pack/plugins/licensing/server/routes/internal/notify_feature_usage.ts @@ -22,7 +22,7 @@ export function registerNotifyFeatureUsageRoute(router: LicensingRouter) { async (context, request, response) => { const { featureName, lastUsed } = request.body; - context.licensing.featureUsage.notifyUsage(featureName, lastUsed); + (await context.licensing).featureUsage.notifyUsage(featureName, lastUsed); return response.ok({ body: { diff --git a/x-pack/plugins/licensing/server/types.ts b/x-pack/plugins/licensing/server/types.ts index 59bb2dd5b104..83b39cb66371 100644 --- a/x-pack/plugins/licensing/server/types.ts +++ b/x-pack/plugins/licensing/server/types.ts @@ -6,7 +6,7 @@ */ import { Observable } from 'rxjs'; -import type { IClusterClient, IRouter, RequestHandlerContext } from '@kbn/core/server'; +import type { IClusterClient, IRouter, CustomRequestHandlerContext } from '@kbn/core/server'; import { ILicense } from '../common/types'; import { FeatureUsageServiceSetup, FeatureUsageServiceStart } from './services'; @@ -35,9 +35,9 @@ export interface LicensingApiRequestHandlerContext { /** * @internal */ -export interface LicensingRequestHandlerContext extends RequestHandlerContext { +export type LicensingRequestHandlerContext = CustomRequestHandlerContext<{ licensing: LicensingApiRequestHandlerContext; -} +}>; /** * @internal diff --git a/x-pack/plugins/licensing/server/wrap_route_with_license_check.ts b/x-pack/plugins/licensing/server/wrap_route_with_license_check.ts index 2358e24b6793..0adcf3f3b612 100644 --- a/x-pack/plugins/licensing/server/wrap_route_with_license_check.ts +++ b/x-pack/plugins/licensing/server/wrap_route_with_license_check.ts @@ -28,7 +28,8 @@ export function wrapRouteWithLicenseCheck, response: KibanaResponseFactory ) => { - const licenseCheckResult = checkLicense(context.licensing.license); + const { license } = await context.licensing; + const licenseCheckResult = checkLicense(license); if (licenseCheckResult.valid) { return handler(context, request, response); diff --git a/x-pack/plugins/lists/public/exceptions/components/builder/builder.stories.tsx b/x-pack/plugins/lists/public/exceptions/components/builder/builder.stories.tsx index ff2efccee90a..bb345f481112 100644 --- a/x-pack/plugins/lists/public/exceptions/components/builder/builder.stories.tsx +++ b/x-pack/plugins/lists/public/exceptions/components/builder/builder.stories.tsx @@ -8,7 +8,7 @@ import { Story, addDecorator } from '@storybook/react'; import React from 'react'; import { HttpStart } from '@kbn/core/public'; -import type { AutocompleteStart } from '@kbn/data-plugin/public'; +import type { AutocompleteStart } from '@kbn/unified-search-plugin/public'; import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common'; import { fields, getField } from '@kbn/data-plugin/common/mocks'; diff --git a/x-pack/plugins/lists/public/exceptions/components/builder/entry_renderer.stories.tsx b/x-pack/plugins/lists/public/exceptions/components/builder/entry_renderer.stories.tsx index 6388a9e5b705..2fbc89e719eb 100644 --- a/x-pack/plugins/lists/public/exceptions/components/builder/entry_renderer.stories.tsx +++ b/x-pack/plugins/lists/public/exceptions/components/builder/entry_renderer.stories.tsx @@ -13,7 +13,7 @@ import { ListOperatorEnum as OperatorEnum, ListOperatorTypeEnum as OperatorTypeEnum, } from '@kbn/securitysolution-io-ts-list-types'; -import type { AutocompleteStart } from '@kbn/data-plugin/public'; +import type { AutocompleteStart } from '@kbn/unified-search-plugin/public'; import { fields } from '@kbn/data-plugin/common/mocks'; import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common'; diff --git a/x-pack/plugins/lists/public/exceptions/components/builder/entry_renderer.test.tsx b/x-pack/plugins/lists/public/exceptions/components/builder/entry_renderer.test.tsx index 4409bd0448ec..d0d931387f0e 100644 --- a/x-pack/plugins/lists/public/exceptions/components/builder/entry_renderer.test.tsx +++ b/x-pack/plugins/lists/public/exceptions/components/builder/entry_renderer.test.tsx @@ -5,10 +5,9 @@ * 2.0. */ -import { ReactWrapper, mount } from 'enzyme'; import React from 'react'; import { EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui'; -import { waitFor } from '@testing-library/dom'; +import { coreMock } from '@kbn/core/public/mocks'; import { doesNotExistOperator, existsOperator, @@ -24,8 +23,9 @@ import { validateFilePathInput } from '@kbn/securitysolution-utils'; import { useFindLists } from '@kbn/securitysolution-list-hooks'; import type { FieldSpec } from '@kbn/data-plugin/common'; import { fields, getField } from '@kbn/data-plugin/common/mocks'; -import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; -import { coreMock } from '@kbn/core/public/mocks'; +import { unifiedSearchPluginMock } from '@kbn/unified-search-plugin/public/mocks'; +import { waitFor } from '@testing-library/dom'; +import { ReactWrapper, mount } from 'enzyme'; import { getFoundListSchemaMock } from '../../../../common/schemas/response/found_list_schema.mock'; @@ -35,7 +35,7 @@ jest.mock('@kbn/securitysolution-list-hooks'); jest.mock('@kbn/securitysolution-utils'); const mockKibanaHttpService = coreMock.createStart().http; -const { autocomplete: autocompleteStartMock } = dataPluginMock.createStartContract(); +const { autocomplete: autocompleteStartMock } = unifiedSearchPluginMock.createStartContract(); describe('BuilderEntryItem', () => { let wrapper: ReactWrapper; diff --git a/x-pack/plugins/lists/public/exceptions/components/builder/entry_renderer.tsx b/x-pack/plugins/lists/public/exceptions/components/builder/entry_renderer.tsx index 3b65042866c5..a28686595053 100644 --- a/x-pack/plugins/lists/public/exceptions/components/builder/entry_renderer.tsx +++ b/x-pack/plugins/lists/public/exceptions/components/builder/entry_renderer.tsx @@ -44,7 +44,7 @@ import { validateFilePathInput, } from '@kbn/securitysolution-utils'; import { DataViewBase, DataViewFieldBase } from '@kbn/es-query'; -import type { AutocompleteStart } from '@kbn/data-plugin/public'; +import type { AutocompleteStart } from '@kbn/unified-search-plugin/public'; import { HttpStart } from '@kbn/core/public'; import { getEmptyValue } from '../../../common/empty_value'; diff --git a/x-pack/plugins/lists/public/exceptions/components/builder/exception_item_renderer.test.tsx b/x-pack/plugins/lists/public/exceptions/components/builder/exception_item_renderer.test.tsx index 65c1e3821598..4041c8516ee2 100644 --- a/x-pack/plugins/lists/public/exceptions/components/builder/exception_item_renderer.test.tsx +++ b/x-pack/plugins/lists/public/exceptions/components/builder/exception_item_renderer.test.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { mount } from 'enzyme'; -import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; +import { unifiedSearchPluginMock } from '@kbn/unified-search-plugin/public/mocks'; import { fields } from '@kbn/data-plugin/common/mocks'; import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common'; import { coreMock } from '@kbn/core/public/mocks'; @@ -19,7 +19,7 @@ import { getEntryMatchAnyMock } from '../../../../common/schemas/types/entry_mat import { BuilderExceptionListItemComponent } from './exception_item_renderer'; const mockKibanaHttpService = coreMock.createStart().http; -const { autocomplete: autocompleteStartMock } = dataPluginMock.createStartContract(); +const { autocomplete: autocompleteStartMock } = unifiedSearchPluginMock.createStartContract(); describe('BuilderExceptionListItemComponent', () => { const getValueSuggestionsMock = jest.fn().mockResolvedValue(['value 1', 'value 2']); diff --git a/x-pack/plugins/lists/public/exceptions/components/builder/exception_item_renderer.tsx b/x-pack/plugins/lists/public/exceptions/components/builder/exception_item_renderer.tsx index a8b685023975..d55289d01688 100644 --- a/x-pack/plugins/lists/public/exceptions/components/builder/exception_item_renderer.tsx +++ b/x-pack/plugins/lists/public/exceptions/components/builder/exception_item_renderer.tsx @@ -8,8 +8,8 @@ import React, { useCallback, useMemo } from 'react'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import styled from 'styled-components'; +import type { AutocompleteStart } from '@kbn/unified-search-plugin/public'; import { HttpStart } from '@kbn/core/public'; -import type { AutocompleteStart } from '@kbn/data-plugin/public'; import { ExceptionListType, OsTypeArray } from '@kbn/securitysolution-io-ts-list-types'; import { BuilderEntry, diff --git a/x-pack/plugins/lists/public/exceptions/components/builder/exception_items_renderer.test.tsx b/x-pack/plugins/lists/public/exceptions/components/builder/exception_items_renderer.test.tsx index 5e86979432f6..e30c5e5cd551 100644 --- a/x-pack/plugins/lists/public/exceptions/components/builder/exception_items_renderer.test.tsx +++ b/x-pack/plugins/lists/public/exceptions/components/builder/exception_items_renderer.test.tsx @@ -8,8 +8,8 @@ import React from 'react'; import { ReactWrapper, mount } from 'enzyme'; import { waitFor } from '@testing-library/react'; +import { unifiedSearchPluginMock } from '@kbn/unified-search-plugin/public/mocks'; import { coreMock } from '@kbn/core/public/mocks'; -import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common'; import { fields, getField } from '@kbn/data-plugin/common/mocks'; @@ -20,7 +20,7 @@ import { getEmptyValue } from '../../../common/empty_value'; import { ExceptionBuilderComponent } from './exception_items_renderer'; const mockKibanaHttpService = coreMock.createStart().http; -const { autocomplete: autocompleteStartMock } = dataPluginMock.createStartContract(); +const { autocomplete: autocompleteStartMock } = unifiedSearchPluginMock.createStartContract(); describe('ExceptionBuilderComponent', () => { let wrapper: ReactWrapper; diff --git a/x-pack/plugins/lists/public/exceptions/components/builder/exception_items_renderer.tsx b/x-pack/plugins/lists/public/exceptions/components/builder/exception_items_renderer.tsx index 8a04ebed888d..69467d7ecea8 100644 --- a/x-pack/plugins/lists/public/exceptions/components/builder/exception_items_renderer.tsx +++ b/x-pack/plugins/lists/public/exceptions/components/builder/exception_items_renderer.tsx @@ -32,7 +32,7 @@ import { getNewExceptionItem, } from '@kbn/securitysolution-list-utils'; import { DataViewBase } from '@kbn/es-query'; -import type { AutocompleteStart } from '@kbn/data-plugin/public'; +import type { AutocompleteStart } from '@kbn/unified-search-plugin/public'; import { AndOrBadge } from '../and_or_badge'; diff --git a/x-pack/plugins/lists/server/plugin.ts b/x-pack/plugins/lists/server/plugin.ts index c5cef49e4873..688469ea063b 100644 --- a/x-pack/plugins/lists/server/plugin.ts +++ b/x-pack/plugins/lists/server/plugin.ts @@ -103,13 +103,11 @@ export class ListPlugin implements Plugin { const { spaces, config, security, extensionPoints } = this; const { - core: { - savedObjects: { client: savedObjectsClient }, - elasticsearch: { - client: { asCurrentUser: esClient }, - }, + savedObjects: { client: savedObjectsClient }, + elasticsearch: { + client: { asCurrentUser: esClient }, }, - } = context; + } = await context.core; if (config == null) { throw new TypeError('Configuration is required for this plugin to operate'); } else { diff --git a/x-pack/plugins/lists/server/routes/create_endpoint_list_item_route.ts b/x-pack/plugins/lists/server/routes/create_endpoint_list_item_route.ts index 46c139d1f373..d6272626618e 100644 --- a/x-pack/plugins/lists/server/routes/create_endpoint_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/create_endpoint_list_item_route.ts @@ -47,7 +47,7 @@ export const createEndpointListItemRoute = (router: ListsPluginRouter): void => os_types: osTypes, type, } = request.body; - const exceptionLists = getExceptionListClient(context); + const exceptionLists = await getExceptionListClient(context); const exceptionListItem = await exceptionLists.getEndpointListItem({ id: undefined, itemId, diff --git a/x-pack/plugins/lists/server/routes/create_endpoint_list_route.ts b/x-pack/plugins/lists/server/routes/create_endpoint_list_route.ts index 134682d3a428..1868a47952a6 100644 --- a/x-pack/plugins/lists/server/routes/create_endpoint_list_route.ts +++ b/x-pack/plugins/lists/server/routes/create_endpoint_list_route.ts @@ -36,7 +36,7 @@ export const createEndpointListRoute = (router: ListsPluginRouter): void => { async (context, _, response) => { const siemResponse = buildSiemResponse(response); try { - const exceptionLists = getExceptionListClient(context); + const exceptionLists = await getExceptionListClient(context); const createdList = await exceptionLists.createEndpointList(); // We always return ok on a create endpoint list route but with an empty body as // an additional fetch of the full list would be slower and the UI has everything hard coded diff --git a/x-pack/plugins/lists/server/routes/create_exception_list_item_route.ts b/x-pack/plugins/lists/server/routes/create_exception_list_item_route.ts index 968f341b9dec..1659fc2645f1 100644 --- a/x-pack/plugins/lists/server/routes/create_exception_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/create_exception_list_item_route.ts @@ -51,7 +51,7 @@ export const createExceptionListItemRoute = (router: ListsPluginRouter): void => os_types: osTypes, type, } = request.body; - const exceptionLists = getExceptionListClient(context); + const exceptionLists = await getExceptionListClient(context); const exceptionList = await exceptionLists.getExceptionList({ id: undefined, listId, diff --git a/x-pack/plugins/lists/server/routes/create_exception_list_route.ts b/x-pack/plugins/lists/server/routes/create_exception_list_route.ts index 22be6432a70c..8d59d8492c82 100644 --- a/x-pack/plugins/lists/server/routes/create_exception_list_route.ts +++ b/x-pack/plugins/lists/server/routes/create_exception_list_route.ts @@ -46,7 +46,7 @@ export const createExceptionListRoute = (router: ListsPluginRouter): void => { type, version, } = request.body; - const exceptionLists = getExceptionListClient(context); + const exceptionLists = await getExceptionListClient(context); const exceptionList = await exceptionLists.getExceptionList({ id: undefined, listId, diff --git a/x-pack/plugins/lists/server/routes/create_list_index_route.ts b/x-pack/plugins/lists/server/routes/create_list_index_route.ts index cebd6140c36a..ee6a3003a0f0 100644 --- a/x-pack/plugins/lists/server/routes/create_list_index_route.ts +++ b/x-pack/plugins/lists/server/routes/create_list_index_route.ts @@ -29,7 +29,7 @@ export const createListIndexRoute = (router: ListsPluginRouter): void => { const siemResponse = buildSiemResponse(response); try { - const lists = getListClient(context); + const lists = await getListClient(context); const listIndexExists = await lists.getListIndexExists(); const listItemIndexExists = await lists.getListItemIndexExists(); diff --git a/x-pack/plugins/lists/server/routes/create_list_item_route.ts b/x-pack/plugins/lists/server/routes/create_list_item_route.ts index bbdc507ebdb6..de798aea97fd 100644 --- a/x-pack/plugins/lists/server/routes/create_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/create_list_item_route.ts @@ -31,7 +31,7 @@ export const createListItemRoute = (router: ListsPluginRouter): void => { const siemResponse = buildSiemResponse(response); try { const { id, list_id: listId, value, meta } = request.body; - const lists = getListClient(context); + const lists = await getListClient(context); const list = await lists.getList({ id: listId }); if (list == null) { return siemResponse.error({ diff --git a/x-pack/plugins/lists/server/routes/create_list_route.ts b/x-pack/plugins/lists/server/routes/create_list_route.ts index b259fe31b931..5e2ddea4aa4b 100644 --- a/x-pack/plugins/lists/server/routes/create_list_route.ts +++ b/x-pack/plugins/lists/server/routes/create_list_route.ts @@ -38,7 +38,7 @@ export const createListRoute = (router: ListsPluginRouter): void => { try { const { name, description, deserializer, id, serializer, type, meta, version } = request.body; - const lists = getListClient(context); + const lists = await getListClient(context); const listExists = await lists.getListIndexExists(); if (!listExists) { return siemResponse.error({ diff --git a/x-pack/plugins/lists/server/routes/delete_endpoint_list_item_route.ts b/x-pack/plugins/lists/server/routes/delete_endpoint_list_item_route.ts index 650d2d11b0d1..94456cea0c32 100644 --- a/x-pack/plugins/lists/server/routes/delete_endpoint_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/delete_endpoint_list_item_route.ts @@ -40,7 +40,7 @@ export const deleteEndpointListItemRoute = (router: ListsPluginRouter): void => async (context, request, response) => { const siemResponse = buildSiemResponse(response); try { - const exceptionLists = getExceptionListClient(context); + const exceptionLists = await getExceptionListClient(context); const { item_id: itemId, id } = request.query; if (itemId == null && id == null) { return siemResponse.error({ diff --git a/x-pack/plugins/lists/server/routes/delete_exception_list_item_route.ts b/x-pack/plugins/lists/server/routes/delete_exception_list_item_route.ts index 7ec220bfb3a6..5aea87e5e9ef 100644 --- a/x-pack/plugins/lists/server/routes/delete_exception_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/delete_exception_list_item_route.ts @@ -40,7 +40,7 @@ export const deleteExceptionListItemRoute = (router: ListsPluginRouter): void => async (context, request, response) => { const siemResponse = buildSiemResponse(response); try { - const exceptionLists = getExceptionListClient(context); + const exceptionLists = await getExceptionListClient(context); const { item_id: itemId, id, namespace_type: namespaceType } = request.query; if (itemId == null && id == null) { return siemResponse.error({ diff --git a/x-pack/plugins/lists/server/routes/delete_exception_list_route.ts b/x-pack/plugins/lists/server/routes/delete_exception_list_route.ts index b4928d026252..26c45c8055a9 100644 --- a/x-pack/plugins/lists/server/routes/delete_exception_list_route.ts +++ b/x-pack/plugins/lists/server/routes/delete_exception_list_route.ts @@ -40,7 +40,7 @@ export const deleteExceptionListRoute = (router: ListsPluginRouter): void => { async (context, request, response) => { const siemResponse = buildSiemResponse(response); try { - const exceptionLists = getExceptionListClient(context); + const exceptionLists = await getExceptionListClient(context); const { list_id: listId, id, namespace_type: namespaceType } = request.query; if (listId == null && id == null) { return siemResponse.error({ diff --git a/x-pack/plugins/lists/server/routes/delete_list_index_route.ts b/x-pack/plugins/lists/server/routes/delete_list_index_route.ts index 448c6c769216..85c6cf96c85a 100644 --- a/x-pack/plugins/lists/server/routes/delete_list_index_route.ts +++ b/x-pack/plugins/lists/server/routes/delete_list_index_route.ts @@ -45,7 +45,7 @@ export const deleteListIndexRoute = (router: ListsPluginRouter): void => { const siemResponse = buildSiemResponse(response); try { - const lists = getListClient(context); + const lists = await getListClient(context); const listIndexExists = await lists.getListIndexExists(); const listItemIndexExists = await lists.getListItemIndexExists(); diff --git a/x-pack/plugins/lists/server/routes/delete_list_item_route.ts b/x-pack/plugins/lists/server/routes/delete_list_item_route.ts index cd6c0ee038b4..bf03f083bd74 100644 --- a/x-pack/plugins/lists/server/routes/delete_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/delete_list_item_route.ts @@ -35,7 +35,7 @@ export const deleteListItemRoute = (router: ListsPluginRouter): void => { const siemResponse = buildSiemResponse(response); try { const { id, list_id: listId, value } = request.query; - const lists = getListClient(context); + const lists = await getListClient(context); if (id != null) { const deleted = await lists.deleteListItem({ id }); if (deleted == null) { diff --git a/x-pack/plugins/lists/server/routes/delete_list_route.ts b/x-pack/plugins/lists/server/routes/delete_list_route.ts index 6ddcce94d82e..57923699c25e 100644 --- a/x-pack/plugins/lists/server/routes/delete_list_route.ts +++ b/x-pack/plugins/lists/server/routes/delete_list_route.ts @@ -42,8 +42,8 @@ export const deleteListRoute = (router: ListsPluginRouter): void => { async (context, request, response) => { const siemResponse = buildSiemResponse(response); try { - const lists = getListClient(context); - const exceptionLists = getExceptionListClient(context); + const lists = await getListClient(context); + const exceptionLists = await getExceptionListClient(context); const { id, deleteReferences, ignoreReferences } = request.query; let deleteExceptionItemResponses; diff --git a/x-pack/plugins/lists/server/routes/export_exception_list_route.ts b/x-pack/plugins/lists/server/routes/export_exception_list_route.ts index f13e84c6e58d..03c7b416ab54 100644 --- a/x-pack/plugins/lists/server/routes/export_exception_list_route.ts +++ b/x-pack/plugins/lists/server/routes/export_exception_list_route.ts @@ -29,7 +29,7 @@ export const exportExceptionsRoute = (router: ListsPluginRouter): void => { try { const { id, list_id: listId, namespace_type: namespaceType } = request.query; - const exceptionListsClient = getExceptionListClient(context); + const exceptionListsClient = await getExceptionListClient(context); const exportContent = await exceptionListsClient.exportExceptionListAndItems({ id, diff --git a/x-pack/plugins/lists/server/routes/export_list_item_route.ts b/x-pack/plugins/lists/server/routes/export_list_item_route.ts index 413c911560e1..f542909327f1 100644 --- a/x-pack/plugins/lists/server/routes/export_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/export_list_item_route.ts @@ -32,7 +32,7 @@ export const exportListItemRoute = (router: ListsPluginRouter): void => { const siemResponse = buildSiemResponse(response); try { const { list_id: listId } = request.query; - const lists = getListClient(context); + const lists = await getListClient(context); const list = await lists.getList({ id: listId }); if (list == null) { return siemResponse.error({ diff --git a/x-pack/plugins/lists/server/routes/find_endpoint_list_item_route.ts b/x-pack/plugins/lists/server/routes/find_endpoint_list_item_route.ts index 6516e8887738..c91a7d882355 100644 --- a/x-pack/plugins/lists/server/routes/find_endpoint_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/find_endpoint_list_item_route.ts @@ -35,7 +35,7 @@ export const findEndpointListItemRoute = (router: ListsPluginRouter): void => { async (context, request, response) => { const siemResponse = buildSiemResponse(response); try { - const exceptionLists = getExceptionListClient(context); + const exceptionLists = await getExceptionListClient(context); const { filter, page, diff --git a/x-pack/plugins/lists/server/routes/find_exception_list_item_route.ts b/x-pack/plugins/lists/server/routes/find_exception_list_item_route.ts index 67450ca02bb2..b28ebf26b012 100644 --- a/x-pack/plugins/lists/server/routes/find_exception_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/find_exception_list_item_route.ts @@ -35,7 +35,7 @@ export const findExceptionListItemRoute = (router: ListsPluginRouter): void => { async (context, request, response) => { const siemResponse = buildSiemResponse(response); try { - const exceptionLists = getExceptionListClient(context); + const exceptionLists = await getExceptionListClient(context); const { filter, list_id: listId, diff --git a/x-pack/plugins/lists/server/routes/find_exception_list_route.ts b/x-pack/plugins/lists/server/routes/find_exception_list_route.ts index e49b9c39d2f0..9365c07e876a 100644 --- a/x-pack/plugins/lists/server/routes/find_exception_list_route.ts +++ b/x-pack/plugins/lists/server/routes/find_exception_list_route.ts @@ -34,7 +34,7 @@ export const findExceptionListRoute = (router: ListsPluginRouter): void => { async (context, request, response) => { const siemResponse = buildSiemResponse(response); try { - const exceptionLists = getExceptionListClient(context); + const exceptionLists = await getExceptionListClient(context); const { filter, page, diff --git a/x-pack/plugins/lists/server/routes/find_list_item_route.ts b/x-pack/plugins/lists/server/routes/find_list_item_route.ts index b0a4a386e21e..37e3ede6582b 100644 --- a/x-pack/plugins/lists/server/routes/find_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/find_list_item_route.ts @@ -35,7 +35,7 @@ export const findListItemRoute = (router: ListsPluginRouter): void => { async (context, request, response) => { const siemResponse = buildSiemResponse(response); try { - const lists = getListClient(context); + const lists = await getListClient(context); const { cursor, filter: filterOrUndefined, diff --git a/x-pack/plugins/lists/server/routes/find_list_route.ts b/x-pack/plugins/lists/server/routes/find_list_route.ts index 98697cc79030..89ed27941d8a 100644 --- a/x-pack/plugins/lists/server/routes/find_list_route.ts +++ b/x-pack/plugins/lists/server/routes/find_list_route.ts @@ -29,7 +29,7 @@ export const findListRoute = (router: ListsPluginRouter): void => { async (context, request, response) => { const siemResponse = buildSiemResponse(response); try { - const lists = getListClient(context); + const lists = await getListClient(context); const { cursor, filter: filterOrUndefined, diff --git a/x-pack/plugins/lists/server/routes/import_exceptions_route.ts b/x-pack/plugins/lists/server/routes/import_exceptions_route.ts index 9db8c27c5397..78e0d571b2dc 100644 --- a/x-pack/plugins/lists/server/routes/import_exceptions_route.ts +++ b/x-pack/plugins/lists/server/routes/import_exceptions_route.ts @@ -43,7 +43,7 @@ export const importExceptionsRoute = (router: ListsPluginRouter, config: ConfigT }, }, async (context, request, response) => { - const exceptionListsClient = getExceptionListClient(context); + const exceptionListsClient = await getExceptionListClient(context); const siemResponse = buildSiemResponse(response); try { diff --git a/x-pack/plugins/lists/server/routes/import_list_item_route.ts b/x-pack/plugins/lists/server/routes/import_list_item_route.ts index a911a6bc26eb..0af6035e40b5 100644 --- a/x-pack/plugins/lists/server/routes/import_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/import_list_item_route.ts @@ -44,7 +44,7 @@ export const importListItemRoute = (router: ListsPluginRouter, config: ConfigTyp try { const stream = createStreamFromBuffer(request.body); const { deserializer, list_id: listId, serializer, type } = request.query; - const lists = getListClient(context); + const lists = await getListClient(context); const listExists = await lists.getListIndexExists(); if (!listExists) { return siemResponse.error({ diff --git a/x-pack/plugins/lists/server/routes/patch_list_item_route.ts b/x-pack/plugins/lists/server/routes/patch_list_item_route.ts index 7bbcab1b1dd6..45d02f5c9d3a 100644 --- a/x-pack/plugins/lists/server/routes/patch_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/patch_list_item_route.ts @@ -31,7 +31,7 @@ export const patchListItemRoute = (router: ListsPluginRouter): void => { const siemResponse = buildSiemResponse(response); try { const { value, id, meta, _version } = request.body; - const lists = getListClient(context); + const lists = await getListClient(context); const listItem = await lists.updateListItem({ _version, id, diff --git a/x-pack/plugins/lists/server/routes/patch_list_route.ts b/x-pack/plugins/lists/server/routes/patch_list_route.ts index da2ea0b65e41..6b23fb1da897 100644 --- a/x-pack/plugins/lists/server/routes/patch_list_route.ts +++ b/x-pack/plugins/lists/server/routes/patch_list_route.ts @@ -31,7 +31,7 @@ export const patchListRoute = (router: ListsPluginRouter): void => { const siemResponse = buildSiemResponse(response); try { const { name, description, id, meta, _version, version } = request.body; - const lists = getListClient(context); + const lists = await getListClient(context); const list = await lists.updateList({ _version, description, id, meta, name, version }); if (list == null) { return siemResponse.error({ diff --git a/x-pack/plugins/lists/server/routes/read_endpoint_list_item_route.ts b/x-pack/plugins/lists/server/routes/read_endpoint_list_item_route.ts index aed51fbb4a5d..053e74dee48c 100644 --- a/x-pack/plugins/lists/server/routes/read_endpoint_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/read_endpoint_list_item_route.ts @@ -41,7 +41,7 @@ export const readEndpointListItemRoute = (router: ListsPluginRouter): void => { const siemResponse = buildSiemResponse(response); try { const { id, item_id: itemId } = request.query; - const exceptionLists = getExceptionListClient(context); + const exceptionLists = await getExceptionListClient(context); if (id != null || itemId != null) { const exceptionListItem = await exceptionLists.getEndpointListItem({ id, diff --git a/x-pack/plugins/lists/server/routes/read_exception_list_item_route.ts b/x-pack/plugins/lists/server/routes/read_exception_list_item_route.ts index d1afb35d41a3..7ea8c02be96d 100644 --- a/x-pack/plugins/lists/server/routes/read_exception_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/read_exception_list_item_route.ts @@ -41,7 +41,7 @@ export const readExceptionListItemRoute = (router: ListsPluginRouter): void => { const siemResponse = buildSiemResponse(response); try { const { id, item_id: itemId, namespace_type: namespaceType } = request.query; - const exceptionLists = getExceptionListClient(context); + const exceptionLists = await getExceptionListClient(context); if (id != null || itemId != null) { const exceptionListItem = await exceptionLists.getExceptionListItem({ id, diff --git a/x-pack/plugins/lists/server/routes/read_exception_list_route.ts b/x-pack/plugins/lists/server/routes/read_exception_list_route.ts index 3b652db6fab6..22ef6e636720 100644 --- a/x-pack/plugins/lists/server/routes/read_exception_list_route.ts +++ b/x-pack/plugins/lists/server/routes/read_exception_list_route.ts @@ -40,7 +40,7 @@ export const readExceptionListRoute = (router: ListsPluginRouter): void => { const siemResponse = buildSiemResponse(response); try { const { id, list_id: listId, namespace_type: namespaceType } = request.query; - const exceptionLists = getExceptionListClient(context); + const exceptionLists = await getExceptionListClient(context); if (id != null || listId != null) { const exceptionList = await exceptionLists.getExceptionList({ id, diff --git a/x-pack/plugins/lists/server/routes/read_list_index_route.ts b/x-pack/plugins/lists/server/routes/read_list_index_route.ts index 6fd15e628edb..061c8486db35 100644 --- a/x-pack/plugins/lists/server/routes/read_list_index_route.ts +++ b/x-pack/plugins/lists/server/routes/read_list_index_route.ts @@ -29,7 +29,7 @@ export const readListIndexRoute = (router: ListsPluginRouter): void => { const siemResponse = buildSiemResponse(response); try { - const lists = getListClient(context); + const lists = await getListClient(context); const listIndexExists = await lists.getListIndexExists(); const listItemIndexExists = await lists.getListItemIndexExists(); diff --git a/x-pack/plugins/lists/server/routes/read_list_item_route.ts b/x-pack/plugins/lists/server/routes/read_list_item_route.ts index 2cb2c4e04288..a663cfe9b8d0 100644 --- a/x-pack/plugins/lists/server/routes/read_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/read_list_item_route.ts @@ -35,7 +35,7 @@ export const readListItemRoute = (router: ListsPluginRouter): void => { const siemResponse = buildSiemResponse(response); try { const { id, list_id: listId, value } = request.query; - const lists = getListClient(context); + const lists = await getListClient(context); if (id != null) { const listItem = await lists.getListItem({ id }); if (listItem == null) { diff --git a/x-pack/plugins/lists/server/routes/read_list_route.ts b/x-pack/plugins/lists/server/routes/read_list_route.ts index e4806f274d51..86761b5c9883 100644 --- a/x-pack/plugins/lists/server/routes/read_list_route.ts +++ b/x-pack/plugins/lists/server/routes/read_list_route.ts @@ -31,7 +31,7 @@ export const readListRoute = (router: ListsPluginRouter): void => { const siemResponse = buildSiemResponse(response); try { const { id } = request.query; - const lists = getListClient(context); + const lists = await getListClient(context); const list = await lists.getList({ id }); if (list == null) { return siemResponse.error({ diff --git a/x-pack/plugins/lists/server/routes/read_privileges_route.ts b/x-pack/plugins/lists/server/routes/read_privileges_route.ts index 0fc72e6cc434..51ffaa9713b6 100644 --- a/x-pack/plugins/lists/server/routes/read_privileges_route.ts +++ b/x-pack/plugins/lists/server/routes/read_privileges_route.ts @@ -25,8 +25,8 @@ export const readPrivilegesRoute = (router: ListsPluginRouter): void => { async (context, request, response) => { const siemResponse = buildSiemResponse(response); try { - const esClient = context.core.elasticsearch.client.asCurrentUser; - const lists = getListClient(context); + const esClient = (await context.core).elasticsearch.client.asCurrentUser; + const lists = await getListClient(context); const clusterPrivilegesLists = await readPrivileges(esClient, lists.getListIndex()); const clusterPrivilegesListItems = await readPrivileges(esClient, lists.getListItemIndex()); const privileges = merge( diff --git a/x-pack/plugins/lists/server/routes/summary_exception_list_route.ts b/x-pack/plugins/lists/server/routes/summary_exception_list_route.ts index 932681eb30d2..f44161a25d37 100644 --- a/x-pack/plugins/lists/server/routes/summary_exception_list_route.ts +++ b/x-pack/plugins/lists/server/routes/summary_exception_list_route.ts @@ -41,7 +41,7 @@ export const summaryExceptionListRoute = (router: ListsPluginRouter): void => { const siemResponse = buildSiemResponse(response); try { const { id, list_id: listId, namespace_type: namespaceType, filter } = request.query; - const exceptionLists = getExceptionListClient(context); + const exceptionLists = await getExceptionListClient(context); if (id != null || listId != null) { const exceptionListSummary = await exceptionLists.getExceptionListSummary({ filter, diff --git a/x-pack/plugins/lists/server/routes/update_endpoint_list_item_route.ts b/x-pack/plugins/lists/server/routes/update_endpoint_list_item_route.ts index 2c960736460d..e1e117f6c560 100644 --- a/x-pack/plugins/lists/server/routes/update_endpoint_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/update_endpoint_list_item_route.ts @@ -50,7 +50,7 @@ export const updateEndpointListItemRoute = (router: ListsPluginRouter): void => item_id: itemId, tags, } = request.body; - const exceptionLists = getExceptionListClient(context); + const exceptionLists = await getExceptionListClient(context); const exceptionListItem = await exceptionLists.updateEndpointListItem({ _version, comments, diff --git a/x-pack/plugins/lists/server/routes/update_exception_list_item_route.ts b/x-pack/plugins/lists/server/routes/update_exception_list_item_route.ts index 910d78e68061..a997ac132e7c 100644 --- a/x-pack/plugins/lists/server/routes/update_exception_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/update_exception_list_item_route.ts @@ -63,7 +63,7 @@ export const updateExceptionListItemRoute = (router: ListsPluginRouter): void => statusCode: 404, }); } else { - const exceptionLists = getExceptionListClient(context); + const exceptionLists = await getExceptionListClient(context); const exceptionListItem = await exceptionLists.updateExceptionListItem({ _version, comments, diff --git a/x-pack/plugins/lists/server/routes/update_exception_list_route.ts b/x-pack/plugins/lists/server/routes/update_exception_list_route.ts index 43279e1f1045..b41a43a0f7c6 100644 --- a/x-pack/plugins/lists/server/routes/update_exception_list_route.ts +++ b/x-pack/plugins/lists/server/routes/update_exception_list_route.ts @@ -53,7 +53,7 @@ export const updateExceptionListRoute = (router: ListsPluginRouter): void => { type, version, } = request.body; - const exceptionLists = getExceptionListClient(context); + const exceptionLists = await getExceptionListClient(context); if (id == null && listId == null) { return siemResponse.error({ body: 'either id or list_id need to be defined', diff --git a/x-pack/plugins/lists/server/routes/update_list_item_route.ts b/x-pack/plugins/lists/server/routes/update_list_item_route.ts index 07ba8539ae62..37c9848f9ddc 100644 --- a/x-pack/plugins/lists/server/routes/update_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/update_list_item_route.ts @@ -31,7 +31,7 @@ export const updateListItemRoute = (router: ListsPluginRouter): void => { const siemResponse = buildSiemResponse(response); try { const { value, id, meta, _version } = request.body; - const lists = getListClient(context); + const lists = await getListClient(context); const listItem = await lists.updateListItem({ _version, id, diff --git a/x-pack/plugins/lists/server/routes/update_list_route.ts b/x-pack/plugins/lists/server/routes/update_list_route.ts index 47ab77b44419..bbf49df7e689 100644 --- a/x-pack/plugins/lists/server/routes/update_list_route.ts +++ b/x-pack/plugins/lists/server/routes/update_list_route.ts @@ -31,7 +31,7 @@ export const updateListRoute = (router: ListsPluginRouter): void => { const siemResponse = buildSiemResponse(response); try { const { name, description, id, meta, _version, version } = request.body; - const lists = getListClient(context); + const lists = await getListClient(context); const list = await lists.updateList({ _version, description, id, meta, name, version }); if (list == null) { return siemResponse.error({ diff --git a/x-pack/plugins/lists/server/routes/utils/get_exception_list_client.ts b/x-pack/plugins/lists/server/routes/utils/get_exception_list_client.ts index 46a02180d731..e4c948a11dda 100644 --- a/x-pack/plugins/lists/server/routes/utils/get_exception_list_client.ts +++ b/x-pack/plugins/lists/server/routes/utils/get_exception_list_client.ts @@ -9,10 +9,10 @@ import type { ListsRequestHandlerContext } from '../../types'; import { ErrorWithStatusCode } from '../../error_with_status_code'; import { ExceptionListClient } from '../../services/exception_lists/exception_list_client'; -export const getExceptionListClient = ( +export const getExceptionListClient = async ( context: ListsRequestHandlerContext -): ExceptionListClient => { - const exceptionLists = context.lists?.getExceptionListClient(); +): Promise => { + const exceptionLists = (await context.lists)?.getExceptionListClient(); if (exceptionLists == null) { throw new ErrorWithStatusCode('Exception lists is not found as a plugin', 404); } else { diff --git a/x-pack/plugins/lists/server/routes/utils/get_list_client.ts b/x-pack/plugins/lists/server/routes/utils/get_list_client.ts index 1c6e995f3fac..988014868180 100644 --- a/x-pack/plugins/lists/server/routes/utils/get_list_client.ts +++ b/x-pack/plugins/lists/server/routes/utils/get_list_client.ts @@ -9,8 +9,8 @@ import { ListClient } from '../../services/lists/list_client'; import { ErrorWithStatusCode } from '../../error_with_status_code'; import type { ListsRequestHandlerContext } from '../../types'; -export const getListClient = (context: ListsRequestHandlerContext): ListClient => { - const lists = context.lists?.getListClient(); +export const getListClient = async (context: ListsRequestHandlerContext): Promise => { + const lists = (await context.lists)?.getListClient(); if (lists == null) { throw new ErrorWithStatusCode('Lists is not found as a plugin', 404); } else { diff --git a/x-pack/plugins/lists/server/types.ts b/x-pack/plugins/lists/server/types.ts index 15bbe0d47b6c..78fdd0e8534a 100644 --- a/x-pack/plugins/lists/server/types.ts +++ b/x-pack/plugins/lists/server/types.ts @@ -6,10 +6,10 @@ */ import { + CustomRequestHandlerContext, ElasticsearchClient, IContextProvider, IRouter, - RequestHandlerContext, SavedObjectsClientContract, } from '@kbn/core/server'; import type { SecurityPluginStart } from '@kbn/security-plugin/server'; @@ -24,6 +24,7 @@ import type { export type ContextProvider = IContextProvider; export type ListsPluginStart = void; + export interface PluginsStart { security: SecurityPluginStart | undefined | null; spaces: SpacesPluginStart | undefined | null; @@ -60,9 +61,9 @@ export interface ListsApiRequestHandlerContext { /** * @internal */ -export interface ListsRequestHandlerContext extends RequestHandlerContext { +export type ListsRequestHandlerContext = CustomRequestHandlerContext<{ lists?: ListsApiRequestHandlerContext; -} +}>; /** * @internal diff --git a/x-pack/plugins/lists/tsconfig.json b/x-pack/plugins/lists/tsconfig.json index 691c5243d9db..6cfffbbaa742 100644 --- a/x-pack/plugins/lists/tsconfig.json +++ b/x-pack/plugins/lists/tsconfig.json @@ -18,5 +18,6 @@ { "path": "../../../src/core/tsconfig.json" }, { "path": "../spaces/tsconfig.json" }, { "path": "../security/tsconfig.json"}, + { "path": "../../../src/plugins/unified_search/tsconfig.json" } ] } diff --git a/x-pack/plugins/logstash/server/routes/cluster/load.ts b/x-pack/plugins/logstash/server/routes/cluster/load.ts index e3937071cb98..d0967ee12376 100644 --- a/x-pack/plugins/logstash/server/routes/cluster/load.ts +++ b/x-pack/plugins/logstash/server/routes/cluster/load.ts @@ -18,7 +18,7 @@ export function registerClusterLoadRoute(router: LogstashPluginRouter) { }, wrapRouteWithLicenseCheck(checkLicense, async (context, request, response) => { try { - const { client } = context.core.elasticsearch; + const { client } = (await context.core).elasticsearch; const info = await client.asCurrentUser.info(); return response.ok({ body: { diff --git a/x-pack/plugins/logstash/server/routes/pipeline/delete.ts b/x-pack/plugins/logstash/server/routes/pipeline/delete.ts index 1fb21c0e8701..ba9ea73850cc 100644 --- a/x-pack/plugins/logstash/server/routes/pipeline/delete.ts +++ b/x-pack/plugins/logstash/server/routes/pipeline/delete.ts @@ -24,7 +24,7 @@ export function registerPipelineDeleteRoute(router: LogstashPluginRouter) { checkLicense, router.handleLegacyErrors(async (context, request, response) => { const { id } = request.params; - const { client } = context.core.elasticsearch; + const { client } = (await context.core).elasticsearch; try { await client.asCurrentUser.logstash.deletePipeline({ id }); diff --git a/x-pack/plugins/logstash/server/routes/pipeline/load.ts b/x-pack/plugins/logstash/server/routes/pipeline/load.ts index 44811aba0b20..8a657207414d 100644 --- a/x-pack/plugins/logstash/server/routes/pipeline/load.ts +++ b/x-pack/plugins/logstash/server/routes/pipeline/load.ts @@ -26,7 +26,7 @@ export function registerPipelineLoadRoute(router: LogstashPluginRouter) { checkLicense, router.handleLegacyErrors(async (context, request, response) => { const { id } = request.params; - const { client } = context.core.elasticsearch; + const { client } = (await context.core).elasticsearch; const result = await client.asCurrentUser.logstash.getPipeline({ id }, { ignore: [404] }); diff --git a/x-pack/plugins/logstash/server/routes/pipeline/save.ts b/x-pack/plugins/logstash/server/routes/pipeline/save.ts index 703edb95a43f..5c50dac7e050 100644 --- a/x-pack/plugins/logstash/server/routes/pipeline/save.ts +++ b/x-pack/plugins/logstash/server/routes/pipeline/save.ts @@ -42,7 +42,7 @@ export function registerPipelineSaveRoute( username = user?.username; } - const { client } = context.core.elasticsearch; + const { client } = (await context.core).elasticsearch; const pipeline = Pipeline.fromDownstreamJSON(request.body, request.params.id, username); await client.asCurrentUser.logstash.putPipeline({ diff --git a/x-pack/plugins/logstash/server/routes/pipelines/delete.ts b/x-pack/plugins/logstash/server/routes/pipelines/delete.ts index 4aac23a2921f..777b86ea6528 100644 --- a/x-pack/plugins/logstash/server/routes/pipelines/delete.ts +++ b/x-pack/plugins/logstash/server/routes/pipelines/delete.ts @@ -45,7 +45,7 @@ export function registerPipelinesDeleteRoute(router: LogstashPluginRouter) { wrapRouteWithLicenseCheck( checkLicense, router.handleLegacyErrors(async (context, request, response) => { - const client = context.core.elasticsearch.client.asCurrentUser; + const client = (await context.core).elasticsearch.client.asCurrentUser; const results = await deletePipelines(client, request.body.pipelineIds); return response.ok({ body: { results } }); diff --git a/x-pack/plugins/logstash/server/routes/pipelines/list.ts b/x-pack/plugins/logstash/server/routes/pipelines/list.ts index 884b923eaf87..cb1a14cbbd42 100644 --- a/x-pack/plugins/logstash/server/routes/pipelines/list.ts +++ b/x-pack/plugins/logstash/server/routes/pipelines/list.ts @@ -33,7 +33,7 @@ export function registerPipelinesListRoute(router: LogstashPluginRouter) { checkLicense, router.handleLegacyErrors(async (context, request, response) => { try { - const { client } = context.core.elasticsearch; + const { client } = (await context.core).elasticsearch; const pipelinesRecord = (await fetchPipelines(client.asCurrentUser)) as Record< string, any diff --git a/x-pack/plugins/logstash/server/types.ts b/x-pack/plugins/logstash/server/types.ts index 69db9753f1e3..d9a9f90101f4 100644 --- a/x-pack/plugins/logstash/server/types.ts +++ b/x-pack/plugins/logstash/server/types.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { IRouter, RequestHandlerContext } from '@kbn/core/server'; +import type { IRouter, CustomRequestHandlerContext } from '@kbn/core/server'; import type { LicensingApiRequestHandlerContext } from '@kbn/licensing-plugin/server'; export interface PipelineListItemOptions { @@ -18,9 +18,9 @@ export interface PipelineListItemOptions { /** * @internal */ -export interface LogstashRequestHandlerContext extends RequestHandlerContext { +export type LogstashRequestHandlerContext = CustomRequestHandlerContext<{ licensing: LicensingApiRequestHandlerContext; -} +}>; /** * @internal diff --git a/x-pack/plugins/maps/public/kibana_services.ts b/x-pack/plugins/maps/public/kibana_services.ts index ebebf4b3ba7f..22857c623c18 100644 --- a/x-pack/plugins/maps/public/kibana_services.ts +++ b/x-pack/plugins/maps/public/kibana_services.ts @@ -32,7 +32,7 @@ export const getIsCloud = () => isCloudEnabled; export const getIndexNameFormComponent = () => pluginsStart.fileUpload.IndexNameFormComponent; export const getFileUploadComponent = () => pluginsStart.fileUpload.FileUploadComponent; export const getIndexPatternService = () => pluginsStart.data.indexPatterns; -export const getAutocompleteService = () => pluginsStart.data.autocomplete; +export const getAutocompleteService = () => pluginsStart.unifiedSearch.autocomplete; export const getInspector = () => pluginsStart.inspector; export const getFileUpload = () => pluginsStart.fileUpload; export const getUiSettings = () => coreStart.uiSettings; diff --git a/x-pack/plugins/maps/server/data_indexing/indexing_routes.ts b/x-pack/plugins/maps/server/data_indexing/indexing_routes.ts index c08bdc8b0d7e..7f2eacc257fc 100644 --- a/x-pack/plugins/maps/server/data_indexing/indexing_routes.ts +++ b/x-pack/plugins/maps/server/data_indexing/indexing_routes.ts @@ -49,16 +49,17 @@ export function initIndexingRoutes({ }, }, async (context, request, response) => { + const coreContext = await context.core; const { index, mappings } = request.body; const indexPatternsService = await dataPlugin.indexPatterns.indexPatternsServiceFactory( - context.core.savedObjects.client, - context.core.elasticsearch.client.asCurrentUser, + coreContext.savedObjects.client, + coreContext.elasticsearch.client.asCurrentUser, request ); const result = await createDocSource( index, mappings, - context.core.elasticsearch.client, + coreContext.elasticsearch.client, indexPatternsService ); if (result.success) { @@ -92,10 +93,11 @@ export function initIndexingRoutes({ }, }, async (context, request, response) => { + const coreContext = await context.core; const result = await writeDataToIndex( request.body.index, request.body.data, - context.core.elasticsearch.client.asCurrentUser + coreContext.elasticsearch.client.asCurrentUser ); if (result.success) { return response.ok({ body: result }); @@ -123,7 +125,8 @@ export function initIndexingRoutes({ }, async (context, request, response) => { try { - const resp = await context.core.elasticsearch.client.asCurrentUser.delete({ + const coreContext = await context.core; + const resp = await coreContext.elasticsearch.client.asCurrentUser.delete({ index: request.body.index, id: request.params.featureId, refresh: true, @@ -173,9 +176,10 @@ export function initIndexingRoutes({ }, }, async (context, request, response) => { + const coreContext = await context.core; return await getMatchingIndexes( request.query.indexPattern, - context.core.elasticsearch.client, + coreContext.elasticsearch.client, response, logger ); @@ -194,8 +198,9 @@ export function initIndexingRoutes({ async (context, request, response) => { const { index } = request.query; try { + const coreContext = await context.core; const mappingsResp = - await context.core.elasticsearch.client.asCurrentUser.indices.getMapping({ + await coreContext.elasticsearch.client.asCurrentUser.indices.getMapping({ index: request.query.index, }); const isDrawingIndex = diff --git a/x-pack/plugins/maps/server/mvt/get_grid_tile.ts b/x-pack/plugins/maps/server/mvt/get_grid_tile.ts index e88a0f031493..eb0ddc9e1314 100644 --- a/x-pack/plugins/maps/server/mvt/get_grid_tile.ts +++ b/x-pack/plugins/maps/server/mvt/get_grid_tile.ts @@ -57,13 +57,14 @@ export async function getEsGridTile({ runtime_mappings: requestBody.runtime_mappings, }; + const esClient = (await context.core).elasticsearch.client; const tile = await core.executionContext.withContext( makeExecutionContext({ description: 'mvt:get_grid_tile', url, }), async () => { - return await context.core.elasticsearch.client.asCurrentUser.transport.request( + return await esClient.asCurrentUser.transport.request( { method: 'GET', path, diff --git a/x-pack/plugins/maps/server/mvt/get_tile.ts b/x-pack/plugins/maps/server/mvt/get_tile.ts index b4ae9e6b698d..340a71128b43 100644 --- a/x-pack/plugins/maps/server/mvt/get_tile.ts +++ b/x-pack/plugins/maps/server/mvt/get_tile.ts @@ -57,13 +57,14 @@ export async function getEsTile({ track_total_hits: requestBody.size + 1, }; + const esClient = (await context.core).elasticsearch.client; const tile = await core.executionContext.withContext( makeExecutionContext({ description: 'mvt:get_tile', url, }), async () => { - return await context.core.elasticsearch.client.asCurrentUser.transport.request( + return await esClient.asCurrentUser.transport.request( { method: 'GET', path, diff --git a/x-pack/plugins/maps/server/routes.ts b/x-pack/plugins/maps/server/routes.ts index d74deec8eab6..427f403927d5 100644 --- a/x-pack/plugins/maps/server/routes.ts +++ b/x-pack/plugins/maps/server/routes.ts @@ -73,7 +73,8 @@ export async function initRoutes(coreSetup: CoreSetup, logger: Logger): Promise< } try { - const resp = await context.core.elasticsearch.client.asCurrentUser.indices.getSettings({ + const coreContext = await context.core; + const resp = await coreContext.elasticsearch.client.asCurrentUser.indices.getSettings({ index: query.indexPatternTitle, }); const indexPatternSettings = getIndexPatternSettings( diff --git a/x-pack/plugins/ml/kibana.json b/x-pack/plugins/ml/kibana.json index 4218eea4ca72..eb00ca117f01 100644 --- a/x-pack/plugins/ml/kibana.json +++ b/x-pack/plugins/ml/kibana.json @@ -19,7 +19,8 @@ "share", "taskManager", "triggersActionsUi", - "uiActions" + "uiActions", + "unifiedSearch" ], "optionalPlugins": [ "alerting", diff --git a/x-pack/plugins/ml/public/application/app.tsx b/x-pack/plugins/ml/public/application/app.tsx index c4ec667e4359..833a4fade128 100644 --- a/x-pack/plugins/ml/public/application/app.tsx +++ b/x-pack/plugins/ml/public/application/app.tsx @@ -119,7 +119,7 @@ export const renderApp = ( setDependencyCache({ timefilter: deps.data.query.timefilter, fieldFormats: deps.fieldFormats, - autocomplete: deps.data.autocomplete, + autocomplete: deps.unifiedSearch.autocomplete, config: coreStart.uiSettings!, chrome: coreStart.chrome!, docLinks: coreStart.docLinks!, diff --git a/x-pack/plugins/ml/public/application/management/breadcrumbs.ts b/x-pack/plugins/ml/public/application/management/breadcrumbs.ts index 1043a9a00873..514941fda2c4 100644 --- a/x-pack/plugins/ml/public/application/management/breadcrumbs.ts +++ b/x-pack/plugins/ml/public/application/management/breadcrumbs.ts @@ -11,8 +11,8 @@ import { JOBS_LIST_PATH } from './management_urls'; export function getJobsListBreadcrumbs() { return [ { - text: i18n.translate('xpack.ml.jobsList.breadcrumb', { - defaultMessage: 'Jobs', + text: i18n.translate('xpack.ml.management.breadcrumb', { + defaultMessage: 'Machine Learning', }), href: `#${JOBS_LIST_PATH}`, }, diff --git a/x-pack/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/jobs_list_page.tsx b/x-pack/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/jobs_list_page.tsx index b3837ceedeb6..15ea5852ca4b 100644 --- a/x-pack/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/jobs_list_page.tsx +++ b/x-pack/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/jobs_list_page.tsx @@ -37,7 +37,6 @@ import { PLUGIN_ID } from '../../../../../../common/constants/app'; import { checkGetManagementMlJobsResolver } from '../../../../capabilities/check_capabilities'; -import { getDocLinks } from '../../../../util/dependency_cache'; // @ts-ignore undeclared module import { JobsListView } from '../../../../jobs/jobs_list/components/jobs_list_view'; import { DataFrameAnalyticsList } from '../../../../data_frame_analytics/pages/analytics_management/components/analytics_list'; @@ -54,6 +53,7 @@ import { ListingPageUrlState } from '../../../../../../common/types/common'; import { getDefaultDFAListState } from '../../../../data_frame_analytics/pages/analytics_management/page'; import { ExportJobsFlyout, ImportJobsFlyout } from '../../../../components/import_export_jobs'; import type { JobType, MlSavedObjectType } from '../../../../../../common/types/saved_objects'; +import { useMlKibana } from '../../../../contexts/kibana'; interface Tab extends EuiTabbedContentTab { 'data-test-subj': string; @@ -201,30 +201,6 @@ export const JobsListPage: FC<{ return null; } - const anomalyDetectionJobsUrl = getDocLinks().links.ml.anomalyDetectionJobs; - const dataFrameAnalyticsUrl = getDocLinks().links.ml.dataFrameAnalytics; - - const anomalyDetectionDocsLabel = i18n.translate( - 'xpack.ml.management.jobsList.anomalyDetectionDocsLabel', - { - defaultMessage: 'Anomaly detection jobs docs', - } - ); - const analyticsDocsLabel = i18n.translate('xpack.ml.management.jobsList.analyticsDocsLabel', { - defaultMessage: 'Analytics jobs docs', - }); - - const docsLink = ( - - {currentTabId === 'anomaly-detector' ? anomalyDetectionDocsLabel : analyticsDocsLabel} - - ); - function renderTabs() { return ( } - rightSideItems={[docsLink]} + rightSideItems={[]} bottomBorder /> @@ -329,3 +305,35 @@ export const JobsListPage: FC<{ ); }; + +const DocsLink: FC<{ currentTabId: MlSavedObjectType }> = ({ currentTabId }) => { + const { + services: { + docLinks: { + links: { ml }, + }, + }, + } = useMlKibana(); + + let href = ml.anomalyDetectionJobs; + let linkLabel = i18n.translate('xpack.ml.management.jobsList.anomalyDetectionDocsLabel', { + defaultMessage: 'Anomaly detection jobs docs', + }); + + if (currentTabId === 'data-frame-analytics') { + href = ml.dataFrameAnalytics; + linkLabel = i18n.translate('xpack.ml.management.jobsList.analyticsDocsLabel', { + defaultMessage: 'Analytics jobs docs', + }); + } else if (currentTabId === 'trained-model') { + href = ml.trainedModels; + linkLabel = i18n.translate('xpack.ml.management.jobsList.trainedModelsDocsLabel', { + defaultMessage: 'Trained models docs', + }); + } + return ( + + {linkLabel} + + ); +}; diff --git a/x-pack/plugins/ml/public/application/util/dependency_cache.ts b/x-pack/plugins/ml/public/application/util/dependency_cache.ts index e2269524fe6e..3680f8b63b0c 100644 --- a/x-pack/plugins/ml/public/application/util/dependency_cache.ts +++ b/x-pack/plugins/ml/public/application/util/dependency_cache.ts @@ -20,7 +20,7 @@ import type { ChromeRecentlyAccessed, IBasePath, } from '@kbn/core/public'; -import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; +import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; import type { DashboardStart } from '@kbn/dashboard-plugin/public'; import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; import type { DataViewsContract } from '@kbn/data-views-plugin/public'; @@ -38,7 +38,7 @@ export interface DependencyCache { theme: ThemeServiceStart | null; recentlyAccessed: ChromeRecentlyAccessed | null; fieldFormats: FieldFormatsStart | null; - autocomplete: DataPublicPluginStart['autocomplete'] | null; + autocomplete: UnifiedSearchPublicPluginStart['autocomplete'] | null; basePath: IBasePath | null; savedObjectsClient: SavedObjectsClientContract | null; application: ApplicationStart | null; diff --git a/x-pack/plugins/ml/public/plugin.ts b/x-pack/plugins/ml/public/plugin.ts index b45ee5f5d395..1ef7c73d2189 100644 --- a/x-pack/plugins/ml/public/plugin.ts +++ b/x-pack/plugins/ml/public/plugin.ts @@ -16,6 +16,7 @@ import type { import { BehaviorSubject } from 'rxjs'; import { take } from 'rxjs/operators'; +import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; import type { ManagementSetup } from '@kbn/management-plugin/public'; import type { SharePluginSetup, SharePluginStart } from '@kbn/share-plugin/public'; import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; @@ -50,6 +51,7 @@ import { PLUGIN_ICON_SOLUTION, PLUGIN_ID } from '../common/constants/app'; export interface MlStartDependencies { data: DataPublicPluginStart; + unifiedSearch: UnifiedSearchPublicPluginStart; share: SharePluginStart; uiActions: UiActionsStart; spaces?: SpacesPluginStart; @@ -109,6 +111,7 @@ export class MlPlugin implements Plugin { { charts: pluginsStart.charts, data: pluginsStart.data, + unifiedSearch: pluginsStart.unifiedSearch, dashboard: pluginsStart.dashboard, share: pluginsStart.share, security: pluginsSetup.security, diff --git a/x-pack/plugins/ml/server/lib/route_guard.ts b/x-pack/plugins/ml/server/lib/route_guard.ts index cd500cfda17c..fa3fb88a109b 100644 --- a/x-pack/plugins/ml/server/lib/route_guard.ts +++ b/x-pack/plugins/ml/server/lib/route_guard.ts @@ -8,7 +8,7 @@ import type { KibanaRequest, KibanaResponseFactory, - RequestHandlerContext, + CustomRequestHandlerContext, IScopedClusterClient, RequestHandler, SavedObjectsClientContract, @@ -25,9 +25,9 @@ import type { MlLicense } from '../../common/license'; import { MlClient, getMlClient } from './ml_client'; import { getDataViewsServiceFactory } from './data_views_utils'; -type MLRequestHandlerContext = RequestHandlerContext & { +type MLRequestHandlerContext = CustomRequestHandlerContext<{ alerting?: AlertingApiRequestHandlerContext; -}; +}>; type Handler

= (handlerParams: { client: IScopedClusterClient; @@ -73,12 +73,13 @@ export class RouteGuard { public fullLicenseAPIGuard(handler: Handler) { return this._guard(() => this._mlLicense.isFullLicense(), handler); } + public basicLicenseAPIGuard(handler: Handler) { return this._guard(() => this._mlLicense.isMinimumLicense(), handler); } private _guard(check: () => boolean, handler: Handler) { - return ( + return async ( context: MLRequestHandlerContext, request: KibanaRequest, response: KibanaResponseFactory @@ -87,7 +88,8 @@ export class RouteGuard { return response.forbidden(); } - const client = context.core.elasticsearch.client; + const { elasticsearch, savedObjects } = await context.core; + const client = elasticsearch.client; const mlSavedObjectClient = this._getMlSavedObjectClient(request); const internalSavedObjectsClient = this._getInternalSavedObjectClient(); if (mlSavedObjectClient === null || internalSavedObjectsClient === null) { @@ -114,7 +116,7 @@ export class RouteGuard { mlClient: getMlClient(client, mlSavedObjectService), getDataViewsService: getDataViewsServiceFactory( this._getDataViews, - context.core.savedObjects.client, + savedObjects.client, client, request ), diff --git a/x-pack/plugins/ml/server/routes/alerting.ts b/x-pack/plugins/ml/server/routes/alerting.ts index 1dd11375cdd3..59091e1c8dfb 100644 --- a/x-pack/plugins/ml/server/routes/alerting.ts +++ b/x-pack/plugins/ml/server/routes/alerting.ts @@ -36,7 +36,7 @@ export function alertingRoutes( routeGuard.fullLicenseAPIGuard(async ({ mlClient, request, response, client, context }) => { try { const alertingService = sharedServicesProviders.alertingServiceProvider( - context.core.savedObjects.client, + (await context.core).savedObjects.client, request ); diff --git a/x-pack/plugins/ml/server/routes/job_service.ts b/x-pack/plugins/ml/server/routes/job_service.ts index e67095d02290..5504682406a0 100644 --- a/x-pack/plugins/ml/server/routes/job_service.ts +++ b/x-pack/plugins/ml/server/routes/job_service.ts @@ -268,11 +268,8 @@ export function jobServiceRoutes({ router, routeGuard }: RouteInitialization) { }, routeGuard.fullLicenseAPIGuard(async ({ client, mlClient, request, response, context }) => { try { - const { jobsSummary } = jobServiceProvider( - client, - mlClient, - context.alerting?.getRulesClient() - ); + const alerting = await context.alerting; + const { jobsSummary } = jobServiceProvider(client, mlClient, alerting?.getRulesClient()); const { jobIds } = request.body; const resp = await jobsSummary(jobIds); @@ -304,10 +301,11 @@ export function jobServiceRoutes({ router, routeGuard }: RouteInitialization) { }, routeGuard.fullLicenseAPIGuard(async ({ client, mlClient, response, context }) => { try { + const alerting = await context.alerting; const { getJobIdsWithGeo } = jobServiceProvider( client, mlClient, - context.alerting?.getRulesClient() + alerting?.getRulesClient() ); const resp = await getJobIdsWithGeo(); @@ -409,10 +407,11 @@ export function jobServiceRoutes({ router, routeGuard }: RouteInitialization) { }, routeGuard.fullLicenseAPIGuard(async ({ client, mlClient, request, response, context }) => { try { + const alerting = await context.alerting; const { createFullJobsList } = jobServiceProvider( client, mlClient, - context.alerting?.getRulesClient() + alerting?.getRulesClient() ); const { jobIds } = request.body; const resp = await createFullJobsList(jobIds); diff --git a/x-pack/plugins/ml/server/routes/modules.ts b/x-pack/plugins/ml/server/routes/modules.ts index fb8a2efed874..757012ba1404 100644 --- a/x-pack/plugins/ml/server/routes/modules.ts +++ b/x-pack/plugins/ml/server/routes/modules.ts @@ -190,11 +190,12 @@ export function dataRecognizer({ router, routeGuard }: RouteInitialization) { }) => { try { const { indexPatternTitle } = request.params; + const soClient = (await context.core).savedObjects.client; const dataViewService = await getDataViewsService(); const results = await recognize( client, mlClient, - context.core.savedObjects.client, + soClient, dataViewService, mlSavedObjectService, request, @@ -344,11 +345,12 @@ export function dataRecognizer({ router, routeGuard }: RouteInitialization) { // the moduleId will be an empty string. moduleId = undefined; } + const soClient = (await context.core).savedObjects.client; const dataViewService = await getDataViewsService(); const results = await getModule( client, mlClient, - context.core.savedObjects.client, + soClient, dataViewService, mlSavedObjectService, request, @@ -541,13 +543,13 @@ export function dataRecognizer({ router, routeGuard }: RouteInitialization) { estimateModelMemory, applyToAllSpaces, } = request.body as TypeOf; - + const soClient = (await context.core).savedObjects.client; const dataViewService = await getDataViewsService(); const result = await setup( client, mlClient, - context.core.savedObjects.client, + soClient, dataViewService, mlSavedObjectService, request, @@ -648,11 +650,12 @@ export function dataRecognizer({ router, routeGuard }: RouteInitialization) { }) => { try { const { moduleId } = request.params; + const soClient = (await context.core).savedObjects.client; const dataViewService = await getDataViewsService(); const result = await dataRecognizerJobsExist( client, mlClient, - context.core.savedObjects.client, + soClient, dataViewService, mlSavedObjectService, request, diff --git a/x-pack/plugins/monitoring/kibana.json b/x-pack/plugins/monitoring/kibana.json index aec034aba066..9a6be4dd5c3d 100644 --- a/x-pack/plugins/monitoring/kibana.json +++ b/x-pack/plugins/monitoring/kibana.json @@ -7,7 +7,7 @@ "githubTeam": "stack-monitoring-ui" }, "configPath": ["monitoring"], - "requiredPlugins": ["licensing", "features", "data", "navigation", "observability", "dataViews"], + "requiredPlugins": ["licensing", "features", "data", "navigation", "observability", "dataViews", "unifiedSearch"], "optionalPlugins": [ "infra", "usageCollection", diff --git a/x-pack/plugins/monitoring/public/components/kuery_bar/autocomplete_field.tsx b/x-pack/plugins/monitoring/public/components/kuery_bar/autocomplete_field.tsx index f241bdb798f8..ae7129f56114 100644 --- a/x-pack/plugins/monitoring/public/components/kuery_bar/autocomplete_field.tsx +++ b/x-pack/plugins/monitoring/public/components/kuery_bar/autocomplete_field.tsx @@ -7,7 +7,7 @@ import { EuiFieldSearch, EuiOutsideClickDetector, EuiPanel } from '@elastic/eui'; import React from 'react'; -import { QuerySuggestion } from '@kbn/data-plugin/public'; +import { QuerySuggestion } from '@kbn/unified-search-plugin/public'; import { euiStyled } from '@kbn/kibana-react-plugin/common'; import { composeStateUpdaters } from '../../lib/typed_react'; import { SuggestionItem } from './suggestion_item'; diff --git a/x-pack/plugins/monitoring/public/components/kuery_bar/index.tsx b/x-pack/plugins/monitoring/public/components/kuery_bar/index.tsx index 9e5588c94a95..6810f2b2b73b 100644 --- a/x-pack/plugins/monitoring/public/components/kuery_bar/index.tsx +++ b/x-pack/plugins/monitoring/public/components/kuery_bar/index.tsx @@ -9,7 +9,7 @@ import { fromKueryExpression } from '@kbn/es-query'; import { i18n } from '@kbn/i18n'; import React, { useEffect, useState } from 'react'; import type { DataView } from '@kbn/data-views-plugin/public'; -import { QuerySuggestion } from '@kbn/data-plugin/public'; +import { QuerySuggestion } from '@kbn/unified-search-plugin/public'; import { AutocompleteField } from './autocomplete_field'; import { WithKueryAutocompletion } from './with_kuery_autocompletion'; diff --git a/x-pack/plugins/monitoring/public/components/kuery_bar/suggestion_item.tsx b/x-pack/plugins/monitoring/public/components/kuery_bar/suggestion_item.tsx index 47a5114f7917..687ba1ebc5e6 100644 --- a/x-pack/plugins/monitoring/public/components/kuery_bar/suggestion_item.tsx +++ b/x-pack/plugins/monitoring/public/components/kuery_bar/suggestion_item.tsx @@ -8,8 +8,8 @@ import { EuiIcon } from '@elastic/eui'; import { transparentize } from 'polished'; import React from 'react'; +import { QuerySuggestion, QuerySuggestionTypes } from '@kbn/unified-search-plugin/public'; import { euiStyled } from '@kbn/kibana-react-plugin/common'; -import { QuerySuggestion, QuerySuggestionTypes } from '@kbn/data-plugin/public'; interface Props { isSelected?: boolean; diff --git a/x-pack/plugins/monitoring/public/components/kuery_bar/with_kuery_autocompletion.tsx b/x-pack/plugins/monitoring/public/components/kuery_bar/with_kuery_autocompletion.tsx index 6284b5b60e0e..bef933d1431a 100644 --- a/x-pack/plugins/monitoring/public/components/kuery_bar/with_kuery_autocompletion.tsx +++ b/x-pack/plugins/monitoring/public/components/kuery_bar/with_kuery_autocompletion.tsx @@ -6,8 +6,8 @@ */ import React from 'react'; -import { QuerySuggestion, DataPublicPluginStart } from '@kbn/data-plugin/public'; import { DataView } from '@kbn/data-views-plugin/public'; +import { UnifiedSearchPublicPluginStart, QuerySuggestion } from '@kbn/unified-search-plugin/public'; import { withKibana, KibanaReactContextValue, @@ -16,7 +16,9 @@ import { import { RendererFunction } from '../../lib/typed_react'; interface WithKueryAutocompletionLifecycleProps { - kibana: KibanaReactContextValue<{ data: DataPublicPluginStart } & KibanaServices>; + kibana: KibanaReactContextValue< + { unifiedSearch: UnifiedSearchPublicPluginStart } & KibanaServices + >; children: RendererFunction<{ isLoadingSuggestions: boolean; loadSuggestions: (expression: string, cursorPosition: number, maxSuggestions?: number) => void; @@ -63,7 +65,7 @@ class WithKueryAutocompletionComponent extends React.Component< const { indexPattern } = this.props; const language = 'kuery'; const hasQuerySuggestions = - this.props.kibana.services.data?.autocomplete.hasQuerySuggestions(language); + this.props.kibana.services.unifiedSearch?.autocomplete.hasQuerySuggestions(language); if (!hasQuerySuggestions) { return; @@ -78,7 +80,7 @@ class WithKueryAutocompletionComponent extends React.Component< }); const suggestions = - (await this.props.kibana.services.data.autocomplete.getQuerySuggestions({ + (await this.props.kibana.services.unifiedSearch.autocomplete.getQuerySuggestions({ language, query: expression, selectionStart: cursorPosition, diff --git a/x-pack/plugins/monitoring/server/plugin.ts b/x-pack/plugins/monitoring/server/plugin.ts index 794d05b38fc8..62660480cfa3 100644 --- a/x-pack/plugins/monitoring/server/plugin.ts +++ b/x-pack/plugins/monitoring/server/plugin.ts @@ -340,14 +340,16 @@ export class MonitoringPlugin res: KibanaResponseFactory ) => { const plugins = (await getCoreServices())[1]; + const coreContext = await context.core; + const actionContext = await context.actions; const legacyRequest: LegacyRequest = { ...req, logger: this.log, getLogger: this.getLogger, payload: req.body, getKibanaStatsCollector: () => this.legacyShimDependencies.kibanaStatsCollector, - getUiSettingsService: () => context.core.uiSettings.client, - getActionTypeRegistry: () => context.actions?.listTypes(), + getUiSettingsService: () => coreContext.uiSettings.client, + getActionTypeRegistry: () => actionContext?.listTypes(), getRulesClient: () => { try { return plugins.alerting.getRulesClientWithRequest(req); @@ -386,7 +388,7 @@ export class MonitoringPlugin const client = name === 'monitoring' ? cluster.asScoped(req).asCurrentUser - : context.core.elasticsearch.client.asCurrentUser; + : coreContext.elasticsearch.client.asCurrentUser; return await Globals.app.getLegacyClusterShim(client, endpoint, params); }, }), diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/alerts/enable.ts b/x-pack/plugins/monitoring/server/routes/api/v1/alerts/enable.ts index e23e636fe567..918821513756 100644 --- a/x-pack/plugins/monitoring/server/routes/api/v1/alerts/enable.ts +++ b/x-pack/plugins/monitoring/server/routes/api/v1/alerts/enable.ts @@ -24,6 +24,10 @@ export function enableAlertsRoute(server: LegacyServer, npRoute: RouteDependenci }, async (context, request, response) => { try { + const alertingContext = await context.alerting; + const infraContext = await context.infra; + const actionContext = await context.actions; + const alerts = AlertsFactory.getAll(); if (alerts.length) { const { isSufficientlySecure, hasPermanentEncryptionKey } = npRoute.alerting @@ -33,7 +37,7 @@ export function enableAlertsRoute(server: LegacyServer, npRoute: RouteDependenci if (!isSufficientlySecure || !hasPermanentEncryptionKey) { server.log.info( - `Skipping rule creation for "${context.infra.spaceId}" space; Stack Monitoring rules require API keys to be enabled and an encryption key to be configured.` + `Skipping rule creation for "${infraContext.spaceId}" space; Stack Monitoring rules require API keys to be enabled and an encryption key to be configured.` ); return response.ok({ body: { @@ -44,9 +48,9 @@ export function enableAlertsRoute(server: LegacyServer, npRoute: RouteDependenci } } - const rulesClient = context.alerting?.getRulesClient(); - const actionsClient = context.actions?.getActionsClient(); - const types = context.actions?.listTypes(); + const rulesClient = alertingContext?.getRulesClient(); + const actionsClient = actionContext?.getActionsClient(); + const types = actionContext?.listTypes(); if (!rulesClient || !actionsClient || !types) { return response.ok({ body: undefined }); } @@ -92,7 +96,7 @@ export function enableAlertsRoute(server: LegacyServer, npRoute: RouteDependenci } server.log.info( - `Created ${createdAlerts.length} alerts for "${context.infra.spaceId}" space` + `Created ${createdAlerts.length} alerts for "${infraContext.spaceId}" space` ); return response.ok({ body: { createdAlerts, disabledWatcherClusterAlerts } }); diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/alerts/status.ts b/x-pack/plugins/monitoring/server/routes/api/v1/alerts/status.ts index 27b21c342f03..a145d9292163 100644 --- a/x-pack/plugins/monitoring/server/routes/api/v1/alerts/status.ts +++ b/x-pack/plugins/monitoring/server/routes/api/v1/alerts/status.ts @@ -34,7 +34,7 @@ export function alertStatusRoute(server: any, npRoute: RouteDependencies) { try { const { clusterUuid } = request.params; const { alertTypeIds, filters } = request.body; - const rulesClient = context.alerting?.getRulesClient(); + const rulesClient = (await context.alerting)?.getRulesClient(); if (!rulesClient) { return response.ok({ body: undefined }); } diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch_settings/check/internal_monitoring.ts b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch_settings/check/internal_monitoring.ts index d456826176e9..8bee3f273e10 100644 --- a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch_settings/check/internal_monitoring.ts +++ b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch_settings/check/internal_monitoring.ts @@ -43,7 +43,7 @@ const queryBody = { }; const checkLatestMonitoringIsLegacy = async (context: RequestHandlerContext, index: string) => { - const client = context.core.elasticsearch.client.asCurrentUser; + const client = (await context.core).elasticsearch.client.asCurrentUser; const result = await client.search>({ index, body: queryBody, diff --git a/x-pack/plugins/monitoring/server/types.ts b/x-pack/plugins/monitoring/server/types.ts index 1930340acd16..ac4939c437b4 100644 --- a/x-pack/plugins/monitoring/server/types.ts +++ b/x-pack/plugins/monitoring/server/types.ts @@ -10,7 +10,7 @@ import type { IRouter, Logger, ICustomClusterClient, - RequestHandlerContext, + CustomRequestHandlerContext, ElasticsearchClient, } from '@kbn/core/server'; import type Boom from '@hapi/boom'; @@ -59,12 +59,12 @@ export interface PluginsSetup { cloud?: CloudSetup; } -export interface RequestHandlerContextMonitoringPlugin extends RequestHandlerContext { +export type RequestHandlerContextMonitoringPlugin = CustomRequestHandlerContext<{ actions?: ActionsApiRequestHandlerContext; alerting?: AlertingApiRequestHandlerContext; infra: InfraRequestHandlerContext; ruleRegistry?: RacApiRequestHandlerContext; -} +}>; export interface PluginsStart { alerting: AlertingPluginStartContract; @@ -183,6 +183,7 @@ export interface Bucket { export interface Aggregation { buckets: Bucket[]; } + export interface ClusterSettingsReasonResponse { found: boolean; reason?: { @@ -233,10 +234,12 @@ export interface PipelineResponse { throughput?: PipelineMetricsProcessed; }; } + export interface PipelinesResponse { pipelines: PipelineResponse[]; totalPipelineCount: number; } + export interface PipelineMetrics { bucket_size: string; timeRange: { @@ -254,6 +257,7 @@ export interface PipelineMetrics { isDerivative: boolean; }; } + export type PipelineMetricsRes = PipelineMetrics & { data: Array<[number, { [key: string]: number }]>; }; diff --git a/x-pack/plugins/observability/public/pages/alerts/containers/alerts_page/alerts_page.tsx b/x-pack/plugins/observability/public/pages/alerts/containers/alerts_page/alerts_page.tsx index e9256993c585..10315af5874a 100644 --- a/x-pack/plugins/observability/public/pages/alerts/containers/alerts_page/alerts_page.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/containers/alerts_page/alerts_page.tsx @@ -18,6 +18,7 @@ import { useKibana } from '@kbn/kibana-react-plugin/public'; import { loadRuleAggregations } from '@kbn/triggers-actions-ui-plugin/public'; import { ParsedTechnicalFields } from '@kbn/rule-registry-plugin/common/parse_technical_fields'; import { ParsedExperimentalFields } from '@kbn/rule-registry-plugin/common/parse_experimental_fields'; +import { Storage } from '@kbn/kibana-utils-plugin/public'; import { AlertStatusFilterButton } from '../../../../../common/typings'; import { useGetUserCasesPermissions } from '../../../../hooks/use_get_user_cases_permissions'; import { observabilityFeatureId } from '../../../../../common'; @@ -70,6 +71,8 @@ const ALERT_STATUS_REGEX = new RegExp( 'gm' ); +const ALERT_TABLE_STATE_STORAGE_KEY = 'xpack.observability.alert.tableState'; + function AlertsPage() { const { ObservabilityPageTemplate, config } = usePluginContext(); const [alertFilterStatus, setAlertFilterStatus] = useState('' as AlertStatusFilterButton); @@ -330,6 +333,8 @@ function AlertsPage() { rangeTo={rangeTo} kuery={kuery} setRefetch={setRefetch} + stateStorageKey={ALERT_TABLE_STATE_STORAGE_KEY} + storage={new Storage(window.localStorage)} /> diff --git a/x-pack/plugins/observability/public/pages/alerts/containers/alerts_table_t_grid/alerts_table_t_grid.tsx b/x-pack/plugins/observability/public/pages/alerts/containers/alerts_table_t_grid/alerts_table_t_grid.tsx index a151cfabf280..32c604ab1973 100644 --- a/x-pack/plugins/observability/public/pages/alerts/containers/alerts_table_t_grid/alerts_table_t_grid.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/containers/alerts_table_t_grid/alerts_table_t_grid.tsx @@ -34,6 +34,8 @@ import { EuiToolTip, } from '@elastic/eui'; +import { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; + import styled from 'styled-components'; import React, { Suspense, useMemo, useState, useCallback, useEffect } from 'react'; @@ -68,13 +70,13 @@ import { addDisplayNames } from './add_display_names'; import { ADD_TO_EXISTING_CASE, ADD_TO_NEW_CASE } from './translations'; import { ObservabilityAppServices } from '../../../../application/types'; -const ALERT_TABLE_STATE_STORAGE_KEY = 'xpack.observability.alert.tableState'; - interface AlertsTableTGridProps { indexNames: string[]; rangeFrom: string; rangeTo: string; kuery?: string; + stateStorageKey: string; + storage: IStorageWrapper; setRefetch: (ref: () => void) => void; } @@ -302,7 +304,7 @@ const FIELDS_WITHOUT_CELL_ACTIONS = [ ]; export function AlertsTableTGrid(props: AlertsTableTGridProps) { - const { indexNames, rangeFrom, rangeTo, kuery, setRefetch } = props; + const { indexNames, rangeFrom, rangeTo, kuery, setRefetch, stateStorageKey, storage } = props; const { timelines, @@ -311,7 +313,7 @@ export function AlertsTableTGrid(props: AlertsTableTGridProps) { const [flyoutAlert, setFlyoutAlert] = useState(undefined); const [tGridState, setTGridState] = useState | null>( - JSON.parse(localStorage.getItem(ALERT_TABLE_STATE_STORAGE_KEY) ?? 'null') + storage.get(stateStorageKey) ); const casePermissions = useGetUserCasesPermissions(); @@ -330,17 +332,17 @@ export function AlertsTableTGrid(props: AlertsTableTGridProps) { useEffect(() => { if (tGridState) { - const newState = JSON.stringify({ + const newState = { ...tGridState, columns: tGridState.columns?.map((c) => pick(c, ['columnHeaderType', 'displayAsText', 'id', 'initialWidth', 'linkField']) ), - }); - if (newState !== localStorage.getItem(ALERT_TABLE_STATE_STORAGE_KEY)) { - localStorage.setItem(ALERT_TABLE_STATE_STORAGE_KEY, newState); + }; + if (newState !== storage.get(stateStorageKey)) { + storage.set(stateStorageKey, newState); } } - }, [tGridState]); + }, [tGridState, stateStorageKey, storage]); const setEventsDeleted = useCallback((action) => { if (action.isDeleted) { diff --git a/x-pack/plugins/observability/public/pages/overview/old_overview_page.tsx b/x-pack/plugins/observability/public/pages/overview/old_overview_page.tsx index 4afe1fe7dedb..0c95362b15db 100644 --- a/x-pack/plugins/observability/public/pages/overview/old_overview_page.tsx +++ b/x-pack/plugins/observability/public/pages/overview/old_overview_page.tsx @@ -21,6 +21,7 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import React, { useMemo, useRef, useCallback, useState, useEffect } from 'react'; import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { Storage } from '@kbn/kibana-utils-plugin/public'; import { observabilityFeatureId } from '../../../common'; import { useTrackPageview, useUiTracker } from '../..'; import { EmptySections } from '../../components/app/empty_sections'; @@ -61,6 +62,8 @@ function calculateBucketSize({ start, end }: { start?: number; end?: number }) { } } +const ALERT_TABLE_STATE_STORAGE_KEY = 'xpack.observability.overview.alert.tableState'; + export function OverviewPage({ routeParams }: Props) { const trackMetric = useUiTracker({ app: 'observability-overview' }); useTrackPageview({ app: 'observability-overview', path: 'overview' }); @@ -195,6 +198,8 @@ export function OverviewPage({ routeParams }: Props) { rangeFrom={relativeStart} rangeTo={relativeEnd} indexNames={indexNames} + stateStorageKey={ALERT_TABLE_STATE_STORAGE_KEY} + storage={new Storage(window.localStorage)} /> diff --git a/x-pack/plugins/observability/server/lib/annotations/bootstrap_annotations.ts b/x-pack/plugins/observability/server/lib/annotations/bootstrap_annotations.ts index 45fe9fd4b920..cb3f350cb24f 100644 --- a/x-pack/plugins/observability/server/lib/annotations/bootstrap_annotations.ts +++ b/x-pack/plugins/observability/server/lib/annotations/bootstrap_annotations.ts @@ -25,7 +25,7 @@ export type ScopedAnnotationsClientFactory = Awaited< ReturnType >['getScopedAnnotationsClient']; -export type ScopedAnnotationsClient = ReturnType; +export type ScopedAnnotationsClient = Awaited>; export type AnnotationsAPI = Awaited>; export async function bootstrapAnnotations({ index, core, context }: Params) { @@ -38,15 +38,19 @@ export async function bootstrapAnnotations({ index, core, context }: Params) { }); return { - getScopedAnnotationsClient: ( - requestContext: RequestHandlerContext & { licensing: LicensingApiRequestHandlerContext }, + getScopedAnnotationsClient: async ( + requestContext: RequestHandlerContext & { + licensing: Promise; + }, request: KibanaRequest ) => { + const esClient = (await requestContext.core).elasticsearch.client; + const { license } = await requestContext.licensing; return createAnnotationsClient({ index, - esClient: requestContext.core.elasticsearch.client.asCurrentUser, + esClient: esClient.asCurrentUser, logger, - license: requestContext.licensing?.license, + license, }); }, }; diff --git a/x-pack/plugins/observability/server/lib/annotations/register_annotation_apis.ts b/x-pack/plugins/observability/server/lib/annotations/register_annotation_apis.ts index 216c83feb53d..6eb0ba6d6e96 100644 --- a/x-pack/plugins/observability/server/lib/annotations/register_annotation_apis.ts +++ b/x-pack/plugins/observability/server/lib/annotations/register_annotation_apis.ts @@ -57,13 +57,14 @@ export function registerAnnotationAPIs({ }); } - const esClient = context.core.elasticsearch.client.asCurrentUser; + const esClient = (await context.core).elasticsearch.client.asCurrentUser; + const license = (await context.licensing)?.license; const client = createAnnotationsClient({ index, esClient, logger, - license: context.licensing?.license, + license, }); try { diff --git a/x-pack/plugins/observability/server/types.ts b/x-pack/plugins/observability/server/types.ts index 0a3e2054f921..75f67d933f68 100644 --- a/x-pack/plugins/observability/server/types.ts +++ b/x-pack/plugins/observability/server/types.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { IRouter, RequestHandlerContext } from '@kbn/core/server'; +import type { IRouter, CustomRequestHandlerContext } from '@kbn/core/server'; import type { AlertingApiRequestHandlerContext } from '@kbn/alerting-plugin/server'; import type { LicensingApiRequestHandlerContext } from '@kbn/licensing-plugin/server'; @@ -20,10 +20,10 @@ export type { /** * @internal */ -export interface ObservabilityRequestHandlerContext extends RequestHandlerContext { +export type ObservabilityRequestHandlerContext = CustomRequestHandlerContext<{ licensing: LicensingApiRequestHandlerContext; alerting: AlertingApiRequestHandlerContext; -} +}>; /** * @internal diff --git a/x-pack/plugins/osquery/server/routes/action/create_action_route.ts b/x-pack/plugins/osquery/server/routes/action/create_action_route.ts index ee9338fc5010..a67b6ee95b9d 100644 --- a/x-pack/plugins/osquery/server/routes/action/create_action_route.ts +++ b/x-pack/plugins/osquery/server/routes/action/create_action_route.ts @@ -35,8 +35,9 @@ export const createActionRoute = (router: IRouter, osqueryContext: OsqueryAppCon }, }, async (context, request, response) => { - const esClient = context.core.elasticsearch.client.asInternalUser; - const soClient = context.core.savedObjects.client; + const coreContext = await context.core; + const esClient = coreContext.elasticsearch.client.asInternalUser; + const soClient = coreContext.savedObjects.client; const internalSavedObjectsClient = await getInternalSavedObjectsClient( osqueryContext.getStartServices ); diff --git a/x-pack/plugins/osquery/server/routes/asset/get_assets_status_route.ts b/x-pack/plugins/osquery/server/routes/asset/get_assets_status_route.ts index d2b590f08f45..2b8c8b0de23d 100644 --- a/x-pack/plugins/osquery/server/routes/asset/get_assets_status_route.ts +++ b/x-pack/plugins/osquery/server/routes/asset/get_assets_status_route.ts @@ -25,7 +25,7 @@ export const getAssetsStatusRoute = (router: IRouter, osqueryContext: OsqueryApp options: { tags: [`access:${PLUGIN_ID}-writePacks`] }, }, async (context, request, response) => { - const savedObjectsClient = context.core.savedObjects.client; + const savedObjectsClient = (await context.core).savedObjects.client; let installation; diff --git a/x-pack/plugins/osquery/server/routes/asset/update_assets_route.ts b/x-pack/plugins/osquery/server/routes/asset/update_assets_route.ts index a852d02d0115..9990c8dbc7b8 100644 --- a/x-pack/plugins/osquery/server/routes/asset/update_assets_route.ts +++ b/x-pack/plugins/osquery/server/routes/asset/update_assets_route.ts @@ -30,7 +30,7 @@ export const updateAssetsRoute = (router: IRouter, osqueryContext: OsqueryAppCon options: { tags: [`access:${PLUGIN_ID}-writePacks`] }, }, async (context, request, response) => { - const savedObjectsClient = context.core.savedObjects.client; + const savedObjectsClient = (await context.core).savedObjects.client; const currentUser = await osqueryContext.security.authc.getCurrentUser(request)?.username; let installation; diff --git a/x-pack/plugins/osquery/server/routes/pack/create_pack_route.ts b/x-pack/plugins/osquery/server/routes/pack/create_pack_route.ts index 14158a4d5fb0..bf3415207858 100644 --- a/x-pack/plugins/osquery/server/routes/pack/create_pack_route.ts +++ b/x-pack/plugins/osquery/server/routes/pack/create_pack_route.ts @@ -60,8 +60,9 @@ export const createPackRoute = (router: IRouter, osqueryContext: OsqueryAppConte options: { tags: [`access:${PLUGIN_ID}-writePacks`] }, }, async (context, request, response) => { - const esClient = context.core.elasticsearch.client.asCurrentUser; - const savedObjectsClient = context.core.savedObjects.client; + const coreContext = await context.core; + const esClient = coreContext.elasticsearch.client.asCurrentUser; + const savedObjectsClient = coreContext.savedObjects.client; const internalSavedObjectsClient = await getInternalSavedObjectsClient( osqueryContext.getStartServices ); diff --git a/x-pack/plugins/osquery/server/routes/pack/delete_pack_route.ts b/x-pack/plugins/osquery/server/routes/pack/delete_pack_route.ts index a32a0feac478..9fc8713348c1 100644 --- a/x-pack/plugins/osquery/server/routes/pack/delete_pack_route.ts +++ b/x-pack/plugins/osquery/server/routes/pack/delete_pack_route.ts @@ -28,8 +28,9 @@ export const deletePackRoute = (router: IRouter, osqueryContext: OsqueryAppConte options: { tags: [`access:${PLUGIN_ID}-writePacks`] }, }, async (context, request, response) => { - const esClient = context.core.elasticsearch.client.asCurrentUser; - const savedObjectsClient = context.core.savedObjects.client; + const coreContext = await context.core; + const esClient = coreContext.elasticsearch.client.asCurrentUser; + const savedObjectsClient = coreContext.savedObjects.client; const packagePolicyService = osqueryContext.service.getPackagePolicyService(); const currentPackSO = await savedObjectsClient.get<{ name: string }>( diff --git a/x-pack/plugins/osquery/server/routes/pack/find_pack_route.ts b/x-pack/plugins/osquery/server/routes/pack/find_pack_route.ts index 333a6a7cdeec..b9e2326d941b 100644 --- a/x-pack/plugins/osquery/server/routes/pack/find_pack_route.ts +++ b/x-pack/plugins/osquery/server/routes/pack/find_pack_route.ts @@ -34,7 +34,8 @@ export const findPackRoute = (router: IRouter, osqueryContext: OsqueryAppContext options: { tags: [`access:${PLUGIN_ID}-readPacks`] }, }, async (context, request, response) => { - const savedObjectsClient = context.core.savedObjects.client; + const coreContext = await context.core; + const savedObjectsClient = coreContext.savedObjects.client; const soClientResponse = await savedObjectsClient.find({ type: packSavedObjectType, diff --git a/x-pack/plugins/osquery/server/routes/pack/read_pack_route.ts b/x-pack/plugins/osquery/server/routes/pack/read_pack_route.ts index 53845de3f48a..bd9f27a569af 100644 --- a/x-pack/plugins/osquery/server/routes/pack/read_pack_route.ts +++ b/x-pack/plugins/osquery/server/routes/pack/read_pack_route.ts @@ -29,7 +29,8 @@ export const readPackRoute = (router: IRouter, osqueryContext: OsqueryAppContext options: { tags: [`access:${PLUGIN_ID}-readPacks`] }, }, async (context, request, response) => { - const savedObjectsClient = context.core.savedObjects.client; + const coreContext = await context.core; + const savedObjectsClient = coreContext.savedObjects.client; const { attributes, references, ...rest } = await savedObjectsClient.get( diff --git a/x-pack/plugins/osquery/server/routes/pack/update_pack_route.ts b/x-pack/plugins/osquery/server/routes/pack/update_pack_route.ts index c37e7fb184b5..82d880c70fbd 100644 --- a/x-pack/plugins/osquery/server/routes/pack/update_pack_route.ts +++ b/x-pack/plugins/osquery/server/routes/pack/update_pack_route.ts @@ -70,8 +70,9 @@ export const updatePackRoute = (router: IRouter, osqueryContext: OsqueryAppConte options: { tags: [`access:${PLUGIN_ID}-writePacks`] }, }, async (context, request, response) => { - const esClient = context.core.elasticsearch.client.asCurrentUser; - const savedObjectsClient = context.core.savedObjects.client; + const coreContext = await context.core; + const esClient = coreContext.elasticsearch.client.asCurrentUser; + const savedObjectsClient = coreContext.savedObjects.client; const internalSavedObjectsClient = await getInternalSavedObjectsClient( osqueryContext.getStartServices ); diff --git a/x-pack/plugins/osquery/server/routes/saved_query/create_saved_query_route.ts b/x-pack/plugins/osquery/server/routes/saved_query/create_saved_query_route.ts index 9a62ad3e2a38..e8bcb9d6bb3b 100644 --- a/x-pack/plugins/osquery/server/routes/saved_query/create_saved_query_route.ts +++ b/x-pack/plugins/osquery/server/routes/saved_query/create_saved_query_route.ts @@ -30,7 +30,8 @@ export const createSavedQueryRoute = (router: IRouter, osqueryContext: OsqueryAp options: { tags: [`access:${PLUGIN_ID}-writeSavedQueries`] }, }, async (context, request, response) => { - const savedObjectsClient = context.core.savedObjects.client; + const coreContext = await context.core; + const savedObjectsClient = coreContext.savedObjects.client; // eslint-disable-next-line @typescript-eslint/naming-convention const { id, description, platform, query, version, interval, ecs_mapping } = request.body; diff --git a/x-pack/plugins/osquery/server/routes/saved_query/delete_saved_query_route.ts b/x-pack/plugins/osquery/server/routes/saved_query/delete_saved_query_route.ts index c8c08904a240..c2a2ad7fa861 100644 --- a/x-pack/plugins/osquery/server/routes/saved_query/delete_saved_query_route.ts +++ b/x-pack/plugins/osquery/server/routes/saved_query/delete_saved_query_route.ts @@ -22,7 +22,8 @@ export const deleteSavedQueryRoute = (router: IRouter) => { options: { tags: [`access:${PLUGIN_ID}-writeSavedQueries`] }, }, async (context, request, response) => { - const savedObjectsClient = context.core.savedObjects.client; + const coreContext = await context.core; + const savedObjectsClient = coreContext.savedObjects.client; await savedObjectsClient.delete(savedQuerySavedObjectType, request.params.id, { refresh: 'wait_for', diff --git a/x-pack/plugins/osquery/server/routes/saved_query/find_saved_query_route.ts b/x-pack/plugins/osquery/server/routes/saved_query/find_saved_query_route.ts index cd1200a5d777..a2b85dbf539d 100644 --- a/x-pack/plugins/osquery/server/routes/saved_query/find_saved_query_route.ts +++ b/x-pack/plugins/osquery/server/routes/saved_query/find_saved_query_route.ts @@ -29,7 +29,8 @@ export const findSavedQueryRoute = (router: IRouter) => { options: { tags: [`access:${PLUGIN_ID}-readSavedQueries`] }, }, async (context, request, response) => { - const savedObjectsClient = context.core.savedObjects.client; + const coreContext = await context.core; + const savedObjectsClient = coreContext.savedObjects.client; const savedQueries = await savedObjectsClient.find<{ ecs_mapping: Array<{ field: string; value: string }>; diff --git a/x-pack/plugins/osquery/server/routes/saved_query/read_saved_query_route.ts b/x-pack/plugins/osquery/server/routes/saved_query/read_saved_query_route.ts index 7887fdce22f0..1c206464d1f6 100644 --- a/x-pack/plugins/osquery/server/routes/saved_query/read_saved_query_route.ts +++ b/x-pack/plugins/osquery/server/routes/saved_query/read_saved_query_route.ts @@ -23,7 +23,8 @@ export const readSavedQueryRoute = (router: IRouter) => { options: { tags: [`access:${PLUGIN_ID}-readSavedQueries`] }, }, async (context, request, response) => { - const savedObjectsClient = context.core.savedObjects.client; + const coreContext = await context.core; + const savedObjectsClient = coreContext.savedObjects.client; const savedQuery = await savedObjectsClient.get<{ ecs_mapping: Array<{ key: string; value: Record }>; diff --git a/x-pack/plugins/osquery/server/routes/saved_query/update_saved_query_route.ts b/x-pack/plugins/osquery/server/routes/saved_query/update_saved_query_route.ts index 5ac4ad711c2a..1d2bf153afd7 100644 --- a/x-pack/plugins/osquery/server/routes/saved_query/update_saved_query_route.ts +++ b/x-pack/plugins/osquery/server/routes/saved_query/update_saved_query_route.ts @@ -48,7 +48,8 @@ export const updateSavedQueryRoute = (router: IRouter, osqueryContext: OsqueryAp options: { tags: [`access:${PLUGIN_ID}-writeSavedQueries`] }, }, async (context, request, response) => { - const savedObjectsClient = context.core.savedObjects.client; + const coreContext = await context.core; + const savedObjectsClient = coreContext.savedObjects.client; const currentUser = await osqueryContext.security.authc.getCurrentUser(request)?.username; const { diff --git a/x-pack/plugins/osquery/server/routes/status/create_status_route.ts b/x-pack/plugins/osquery/server/routes/status/create_status_route.ts index 613ad6e7720a..019582addabb 100644 --- a/x-pack/plugins/osquery/server/routes/status/create_status_route.ts +++ b/x-pack/plugins/osquery/server/routes/status/create_status_route.ts @@ -27,7 +27,8 @@ export const createStatusRoute = (router: IRouter, osqueryContext: OsqueryAppCon options: { tags: [`access:${PLUGIN_ID}-read`] }, }, async (context, request, response) => { - const esClient = context.core.elasticsearch.client.asInternalUser; + const coreContext = await context.core; + const esClient = coreContext.elasticsearch.client.asInternalUser; const internalSavedObjectsClient = await getInternalSavedObjectsClient( osqueryContext.getStartServices ); diff --git a/x-pack/plugins/painless_lab/server/routes/api/execute.ts b/x-pack/plugins/painless_lab/server/routes/api/execute.ts index 58cb9f4328d2..88f2156a5fb4 100644 --- a/x-pack/plugins/painless_lab/server/routes/api/execute.ts +++ b/x-pack/plugins/painless_lab/server/routes/api/execute.ts @@ -25,7 +25,7 @@ export function registerExecuteRoute({ router, license }: RouteDependencies) { const body = req.body; try { - const client = ctx.core.elasticsearch.client.asCurrentUser; + const client = (await ctx.core).elasticsearch.client.asCurrentUser; const response = await client.scriptsPainlessExecute( { // @ts-expect-error `ExecutePainlessScriptRequest.body` does not allow `string` diff --git a/x-pack/plugins/remote_clusters/server/routes/api/add_route.test.ts b/x-pack/plugins/remote_clusters/server/routes/api/add_route.test.ts index 3fe04be97abf..a1f256802fb5 100644 --- a/x-pack/plugins/remote_clusters/server/routes/api/add_route.test.ts +++ b/x-pack/plugins/remote_clusters/server/routes/api/add_route.test.ts @@ -104,7 +104,11 @@ describe('ADD remote clusters', () => { const mockRequest = createMockRequest(); - const response = await handler(mockContext, mockRequest, kibanaResponseFactory); + const response = await handler( + coreMock.createCustomRequestHandlerContext(mockContext), + mockRequest, + kibanaResponseFactory + ); expect(response.status).toBe(200); expect(response.payload).toEqual({ acknowledged: true }); @@ -161,7 +165,11 @@ describe('ADD remote clusters', () => { serverName: 'foobar', }); - const response = await handler(mockContext, mockRequest, kibanaResponseFactory); + const response = await handler( + coreMock.createCustomRequestHandlerContext(mockContext), + mockRequest, + kibanaResponseFactory + ); expect(response.status).toBe(200); expect(response.payload).toEqual({ acknowledged: true }); @@ -205,7 +213,11 @@ describe('ADD remote clusters', () => { const mockRequest = createMockRequest(); - const response = await handler(mockContext, mockRequest, kibanaResponseFactory); + const response = await handler( + coreMock.createCustomRequestHandlerContext(mockContext), + mockRequest, + kibanaResponseFactory + ); expect(response.status).toBe(409); expect(response.payload).toEqual({ @@ -222,7 +234,11 @@ describe('ADD remote clusters', () => { const mockRequest = createMockRequest(); - const response = await handler(mockContext, mockRequest, kibanaResponseFactory); + const response = await handler( + coreMock.createCustomRequestHandlerContext(mockContext), + mockRequest, + kibanaResponseFactory + ); expect(response.status).toBe(400); expect(response.payload).toEqual({ diff --git a/x-pack/plugins/remote_clusters/server/routes/api/add_route.ts b/x-pack/plugins/remote_clusters/server/routes/api/add_route.ts index 85e8193a0eb2..50fb15ef3282 100644 --- a/x-pack/plugins/remote_clusters/server/routes/api/add_route.ts +++ b/x-pack/plugins/remote_clusters/server/routes/api/add_route.ts @@ -41,7 +41,7 @@ export const register = (deps: RouteDependencies): void => { response ) => { try { - const { client: clusterClient } = ctx.core.elasticsearch; + const { client: clusterClient } = (await ctx.core).elasticsearch; const { name } = request.body; diff --git a/x-pack/plugins/remote_clusters/server/routes/api/delete_route.test.ts b/x-pack/plugins/remote_clusters/server/routes/api/delete_route.test.ts index 0dc48d82cdb0..ed92c78f9d09 100644 --- a/x-pack/plugins/remote_clusters/server/routes/api/delete_route.test.ts +++ b/x-pack/plugins/remote_clusters/server/routes/api/delete_route.test.ts @@ -117,7 +117,11 @@ describe('DELETE remote clusters', () => { const mockRequest = createMockRequest(); - const response = await handler(mockContext, mockRequest, kibanaResponseFactory); + const response = await handler( + coreMock.createCustomRequestHandlerContext(mockContext), + mockRequest, + kibanaResponseFactory + ); expect(response.status).toBe(200); expect(response.payload).toEqual({ @@ -163,7 +167,11 @@ describe('DELETE remote clusters', () => { const mockRequest = createMockRequest(); - const response = await handler(mockContext, mockRequest, kibanaResponseFactory); + const response = await handler( + coreMock.createCustomRequestHandlerContext(mockContext), + mockRequest, + kibanaResponseFactory + ); expect(response.status).toBe(200); expect(response.payload).toEqual({ @@ -240,7 +248,11 @@ describe('DELETE remote clusters', () => { const mockRequest = createMockRequest(); - const response = await handler(mockContext, mockRequest, kibanaResponseFactory); + const response = await handler( + coreMock.createCustomRequestHandlerContext(mockContext), + mockRequest, + kibanaResponseFactory + ); expect(response.status).toBe(200); expect(response.payload).toEqual({ diff --git a/x-pack/plugins/remote_clusters/server/routes/api/delete_route.ts b/x-pack/plugins/remote_clusters/server/routes/api/delete_route.ts index 9d24efc1a554..8e11a0cbe246 100644 --- a/x-pack/plugins/remote_clusters/server/routes/api/delete_route.ts +++ b/x-pack/plugins/remote_clusters/server/routes/api/delete_route.ts @@ -34,7 +34,7 @@ export const register = (deps: RouteDependencies): void => { response ) => { try { - const { client: clusterClient } = ctx.core.elasticsearch; + const { client: clusterClient } = (await ctx.core).elasticsearch; const { nameOrNames } = request.params; const names = nameOrNames.split(','); diff --git a/x-pack/plugins/remote_clusters/server/routes/api/get_route.test.ts b/x-pack/plugins/remote_clusters/server/routes/api/get_route.test.ts index 9bb877b8bff1..c3628af8c2ef 100644 --- a/x-pack/plugins/remote_clusters/server/routes/api/get_route.test.ts +++ b/x-pack/plugins/remote_clusters/server/routes/api/get_route.test.ts @@ -101,7 +101,11 @@ describe('GET remote clusters', () => { const mockRequest = createMockRequest(); - const response = await handler(mockContext, mockRequest, kibanaResponseFactory); + const response = await handler( + coreMock.createCustomRequestHandlerContext(mockContext), + mockRequest, + kibanaResponseFactory + ); expect(response.status).toBe(200); expect(response.payload).toEqual([ @@ -128,7 +132,11 @@ describe('GET remote clusters', () => { const mockRequest = createMockRequest(); - const response = await handler(mockContext, mockRequest, kibanaResponseFactory); + const response = await handler( + coreMock.createCustomRequestHandlerContext(mockContext), + mockRequest, + kibanaResponseFactory + ); expect(response.status).toBe(200); expect(response.payload).toEqual([]); @@ -152,7 +160,11 @@ describe('GET remote clusters', () => { const mockRequest = createMockRequest(); - const response = await handler(mockContext, mockRequest, kibanaResponseFactory); + const response = await handler( + coreMock.createCustomRequestHandlerContext(mockContext), + mockRequest, + kibanaResponseFactory + ); expect(response.status).toBe(406); expect(response.payload).toEqual({ @@ -191,7 +203,11 @@ describe('GET remote clusters', () => { headers: { authorization: 'foo' }, }); - const response = await handler(mockContext, mockRequest, kibanaResponseFactory); + const response = await handler( + coreMock.createCustomRequestHandlerContext(mockContext), + mockRequest, + kibanaResponseFactory + ); expect(response.status).toBe(406); diff --git a/x-pack/plugins/remote_clusters/server/routes/api/get_route.ts b/x-pack/plugins/remote_clusters/server/routes/api/get_route.ts index 7b12ad8d4418..8923e9be7d9f 100644 --- a/x-pack/plugins/remote_clusters/server/routes/api/get_route.ts +++ b/x-pack/plugins/remote_clusters/server/routes/api/get_route.ts @@ -21,7 +21,7 @@ export const register = (deps: RouteDependencies): void => { const allHandler: RequestHandler = async (ctx, request, response) => { try { - const { client: clusterClient } = ctx.core.elasticsearch; + const { client: clusterClient } = (await ctx.core).elasticsearch; const clusterSettings = await clusterClient.asCurrentUser.cluster.getSettings(); const transientClusterNames = Object.keys( diff --git a/x-pack/plugins/remote_clusters/server/routes/api/update_route.test.ts b/x-pack/plugins/remote_clusters/server/routes/api/update_route.test.ts index 65939be965c1..951d8c5d806e 100644 --- a/x-pack/plugins/remote_clusters/server/routes/api/update_route.test.ts +++ b/x-pack/plugins/remote_clusters/server/routes/api/update_route.test.ts @@ -110,7 +110,11 @@ describe('UPDATE remote clusters', () => { const mockRequest = createMockRequest(); - const response = await handler(mockContext, mockRequest, kibanaResponseFactory); + const response = await handler( + coreMock.createCustomRequestHandlerContext(mockContext), + mockRequest, + kibanaResponseFactory + ); expect(response.status).toBe(200); expect(response.payload).toEqual({ @@ -187,7 +191,11 @@ describe('UPDATE remote clusters', () => { proxySocketConnections: 18, }); - const response = await handler(mockContext, mockRequest, kibanaResponseFactory); + const response = await handler( + coreMock.createCustomRequestHandlerContext(mockContext), + mockRequest, + kibanaResponseFactory + ); expect(response.status).toBe(200); expect(response.payload).toEqual({ @@ -234,7 +242,11 @@ describe('UPDATE remote clusters', () => { mode: 'sniff', }); - const response = await handler(mockContext, mockRequest, kibanaResponseFactory); + const response = await handler( + coreMock.createCustomRequestHandlerContext(mockContext), + mockRequest, + kibanaResponseFactory + ); expect(response.status).toBe(404); expect(response.payload).toEqual({ @@ -265,7 +277,11 @@ describe('UPDATE remote clusters', () => { mode: 'sniff', }); - const response = await handler(mockContext, mockRequest, kibanaResponseFactory); + const response = await handler( + coreMock.createCustomRequestHandlerContext(mockContext), + mockRequest, + kibanaResponseFactory + ); expect(response.status).toBe(400); expect(response.payload).toEqual({ diff --git a/x-pack/plugins/remote_clusters/server/routes/api/update_route.ts b/x-pack/plugins/remote_clusters/server/routes/api/update_route.ts index db9b3184ac1e..e186262ade21 100644 --- a/x-pack/plugins/remote_clusters/server/routes/api/update_route.ts +++ b/x-pack/plugins/remote_clusters/server/routes/api/update_route.ts @@ -47,7 +47,7 @@ export const register = (deps: RouteDependencies): void => { response ) => { try { - const { client: clusterClient } = ctx.core.elasticsearch; + const { client: clusterClient } = (await ctx.core).elasticsearch; const { name } = request.params; diff --git a/x-pack/plugins/reporting/server/export_types/csv_searchsource_immediate/execute_job.ts b/x-pack/plugins/reporting/server/export_types/csv_searchsource_immediate/execute_job.ts index efe905eedd9a..31924d0d89cf 100644 --- a/x-pack/plugins/reporting/server/export_types/csv_searchsource_immediate/execute_job.ts +++ b/x-pack/plugins/reporting/server/export_types/csv_searchsource_immediate/execute_job.ts @@ -39,7 +39,7 @@ export const runTaskFnFactory: RunTaskFnFactory = function e ...immediateJobParams, }; - const savedObjectsClient = context.core.savedObjects.client; + const savedObjectsClient = (await context.core).savedObjects.client; const uiSettings = await reporting.getUiSettingsServiceFactory(savedObjectsClient); const dataPluginStart = await reporting.getDataService(); const fieldFormatsRegistry = await getFieldFormats().fieldFormatServiceFactory(uiSettings); diff --git a/x-pack/plugins/reporting/server/routes/deprecations/deprecations.ts b/x-pack/plugins/reporting/server/routes/deprecations/deprecations.ts index ecb8dc16435f..dd04cd20ddac 100644 --- a/x-pack/plugins/reporting/server/routes/deprecations/deprecations.ts +++ b/x-pack/plugins/reporting/server/routes/deprecations/deprecations.ts @@ -27,9 +27,7 @@ export const registerDeprecationsRoutes = (reporting: ReportingCore, logger: Log return handler(ctx, req, res); } - const { - core: { elasticsearch }, - } = ctx; + const { elasticsearch } = await ctx.core; const store = await reporting.getStore(); @@ -63,47 +61,40 @@ export const registerDeprecationsRoutes = (reporting: ReportingCore, logger: Log path: API_GET_ILM_POLICY_STATUS, validate: false, }, - authzWrapper( - async ( - { - core: { - elasticsearch: { client: scopedClient }, - }, - }, - _req, - res - ) => { - const checkIlmMigrationStatus = () => { - return deprecations.checkIlmMigrationStatus({ - reportingCore: reporting, - // We want to make the current status visible to all reporting users - elasticsearchClient: scopedClient.asInternalUser, - }); - }; + authzWrapper(async ({ core }, _req, res) => { + const { + elasticsearch: { client: scopedClient }, + } = await core; + const checkIlmMigrationStatus = () => { + return deprecations.checkIlmMigrationStatus({ + reportingCore: reporting, + // We want to make the current status visible to all reporting users + elasticsearchClient: scopedClient.asInternalUser, + }); + }; - try { - const response: IlmPolicyStatusResponse = { - status: await checkIlmMigrationStatus(), - }; - return res.ok({ body: response }); - } catch (e) { - logger.error(e); - return res.customError({ - statusCode: e?.statusCode ?? 500, - body: { message: e.message }, - }); - } + try { + const response: IlmPolicyStatusResponse = { + status: await checkIlmMigrationStatus(), + }; + return res.ok({ body: response }); + } catch (e) { + logger.error(e); + return res.customError({ + statusCode: e?.statusCode ?? 500, + body: { message: e.message }, + }); } - ) + }) ); router.put( { path: API_MIGRATE_ILM_POLICY_URL, validate: false }, - authzWrapper(async ({ core: { elasticsearch } }, _req, res) => { + authzWrapper(async ({ core }, _req, res) => { const store = await reporting.getStore(); const { client: { asCurrentUser: client }, - } = elasticsearch; + } = (await core).elasticsearch; const scopedIlmPolicyManager = IlmPolicyManager.create({ client, diff --git a/x-pack/plugins/reporting/server/routes/lib/request_handler.test.ts b/x-pack/plugins/reporting/server/routes/lib/request_handler.test.ts index 97b85f8d2d5c..f3e771555c5a 100644 --- a/x-pack/plugins/reporting/server/routes/lib/request_handler.test.ts +++ b/x-pack/plugins/reporting/server/routes/lib/request_handler.test.ts @@ -76,7 +76,7 @@ describe('Handle request to generate', () => { (mockResponseFactory.badRequest as jest.Mock) = jest.fn((args: unknown) => args); mockContext = getMockContext(); - mockContext.reporting = {} as ReportingSetup; + mockContext.reporting = Promise.resolve({} as ReportingSetup); requestHandler = new RequestHandler( reportingCore, diff --git a/x-pack/plugins/reporting/server/types.ts b/x-pack/plugins/reporting/server/types.ts index b272f9687c14..f660690c6a75 100644 --- a/x-pack/plugins/reporting/server/types.ts +++ b/x-pack/plugins/reporting/server/types.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { IRouter, Logger, RequestHandlerContext } from '@kbn/core/server'; +import type { IRouter, Logger, CustomRequestHandlerContext } from '@kbn/core/server'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import type { DataPluginStart } from '@kbn/data-plugin/server/plugin'; import { FieldFormatsStart } from '@kbn/field-formats-plugin/server'; @@ -113,10 +113,9 @@ export interface ReportingStartDeps { taskManager: TaskManagerStartContract; } -export interface ReportingRequestHandlerContext { +export type ReportingRequestHandlerContext = CustomRequestHandlerContext<{ reporting: ReportingStart | null; - core: RequestHandlerContext['core']; -} +}>; export type ReportingPluginRouter = IRouter; diff --git a/x-pack/plugins/rollup/server/routes/api/indices/register_get_route.ts b/x-pack/plugins/rollup/server/routes/api/indices/register_get_route.ts index 485c1301b4d9..99312936adba 100644 --- a/x-pack/plugins/rollup/server/routes/api/indices/register_get_route.ts +++ b/x-pack/plugins/rollup/server/routes/api/indices/register_get_route.ts @@ -23,7 +23,7 @@ export const registerGetRoute = ({ }, license.guardApiRoute(async (context, request, response) => { try { - const { client: clusterClient } = context.core.elasticsearch; + const { client: clusterClient } = (await context.core).elasticsearch; const data = await clusterClient.asCurrentUser.rollup.getRollupIndexCaps({ index: '_all', }); diff --git a/x-pack/plugins/rollup/server/routes/api/indices/register_validate_index_pattern_route.ts b/x-pack/plugins/rollup/server/routes/api/indices/register_validate_index_pattern_route.ts index dec51870f2f5..8eaea73e42b3 100644 --- a/x-pack/plugins/rollup/server/routes/api/indices/register_validate_index_pattern_route.ts +++ b/x-pack/plugins/rollup/server/routes/api/indices/register_validate_index_pattern_route.ts @@ -67,7 +67,7 @@ export const registerValidateIndexPatternRoute = ({ }, }, license.guardApiRoute(async (context, request, response) => { - const { client: clusterClient } = context.core.elasticsearch; + const { client: clusterClient } = (await context.core).elasticsearch; try { const { indexPattern } = request.params; const [fieldCapabilities, rollupIndexCapabilities] = await Promise.all([ diff --git a/x-pack/plugins/rollup/server/routes/api/jobs/register_create_route.ts b/x-pack/plugins/rollup/server/routes/api/jobs/register_create_route.ts index ca067d083398..21c0681f8458 100644 --- a/x-pack/plugins/rollup/server/routes/api/jobs/register_create_route.ts +++ b/x-pack/plugins/rollup/server/routes/api/jobs/register_create_route.ts @@ -29,7 +29,7 @@ export const registerCreateRoute = ({ }, }, license.guardApiRoute(async (context, request, response) => { - const { client: clusterClient } = context.core.elasticsearch; + const { client: clusterClient } = (await context.core).elasticsearch; try { const { id, ...rest } = request.body.job; // Create job. diff --git a/x-pack/plugins/rollup/server/routes/api/jobs/register_delete_route.ts b/x-pack/plugins/rollup/server/routes/api/jobs/register_delete_route.ts index 7e22b5c4ead1..f6b530ef2fc0 100644 --- a/x-pack/plugins/rollup/server/routes/api/jobs/register_delete_route.ts +++ b/x-pack/plugins/rollup/server/routes/api/jobs/register_delete_route.ts @@ -24,7 +24,7 @@ export const registerDeleteRoute = ({ }, }, license.guardApiRoute(async (context, request, response) => { - const { client: clusterClient } = context.core.elasticsearch; + const { client: clusterClient } = (await context.core).elasticsearch; try { const { jobIds } = request.body; const data = await Promise.all( diff --git a/x-pack/plugins/rollup/server/routes/api/jobs/register_get_route.ts b/x-pack/plugins/rollup/server/routes/api/jobs/register_get_route.ts index cc0b138aeb83..d74a2907254d 100644 --- a/x-pack/plugins/rollup/server/routes/api/jobs/register_get_route.ts +++ b/x-pack/plugins/rollup/server/routes/api/jobs/register_get_route.ts @@ -19,7 +19,7 @@ export const registerGetRoute = ({ validate: false, }, license.guardApiRoute(async (context, request, response) => { - const { client: clusterClient } = context.core.elasticsearch; + const { client: clusterClient } = (await context.core).elasticsearch; try { const data = await clusterClient.asCurrentUser.rollup.getJobs({ id: '_all' }); return response.ok({ body: data }); diff --git a/x-pack/plugins/rollup/server/routes/api/jobs/register_start_route.ts b/x-pack/plugins/rollup/server/routes/api/jobs/register_start_route.ts index 133c0cb34c9f..85e2a508f04a 100644 --- a/x-pack/plugins/rollup/server/routes/api/jobs/register_start_route.ts +++ b/x-pack/plugins/rollup/server/routes/api/jobs/register_start_route.ts @@ -29,7 +29,7 @@ export const registerStartRoute = ({ }, }, license.guardApiRoute(async (context, request, response) => { - const { client: clusterClient } = context.core.elasticsearch; + const { client: clusterClient } = (await context.core).elasticsearch; try { const { jobIds } = request.body; diff --git a/x-pack/plugins/rollup/server/routes/api/jobs/register_stop_route.ts b/x-pack/plugins/rollup/server/routes/api/jobs/register_stop_route.ts index 164273f604b4..617023413eea 100644 --- a/x-pack/plugins/rollup/server/routes/api/jobs/register_stop_route.ts +++ b/x-pack/plugins/rollup/server/routes/api/jobs/register_stop_route.ts @@ -27,7 +27,7 @@ export const registerStopRoute = ({ }, }, license.guardApiRoute(async (context, request, response) => { - const { client: clusterClient } = context.core.elasticsearch; + const { client: clusterClient } = (await context.core).elasticsearch; try { const { jobIds } = request.body; // For our API integration tests we need to wait for the jobs to be stopped diff --git a/x-pack/plugins/rollup/server/routes/api/search/register_search_route.ts b/x-pack/plugins/rollup/server/routes/api/search/register_search_route.ts index ab144c1f4244..98116e828886 100644 --- a/x-pack/plugins/rollup/server/routes/api/search/register_search_route.ts +++ b/x-pack/plugins/rollup/server/routes/api/search/register_search_route.ts @@ -27,7 +27,7 @@ export const registerSearchRoute = ({ }, }, license.guardApiRoute(async (context, request, response) => { - const { client: clusterClient } = context.core.elasticsearch; + const { client: clusterClient } = (await context.core).elasticsearch; try { const requests = request.body.map(({ index, query }: { index: string; query?: any }) => clusterClient.asCurrentUser.rollup.rollupSearch({ diff --git a/x-pack/plugins/rule_registry/server/routes/bulk_update_alerts.ts b/x-pack/plugins/rule_registry/server/routes/bulk_update_alerts.ts index 4d297c56fe28..a1a316200e5e 100644 --- a/x-pack/plugins/rule_registry/server/routes/bulk_update_alerts.ts +++ b/x-pack/plugins/rule_registry/server/routes/bulk_update_alerts.ts @@ -52,7 +52,8 @@ export const bulkUpdateAlertsRoute = (router: IRouter) }, async (context, req, response) => { try { - const alertsClient = await context.rac.getAlertsClient(); + const racContext = await context.rac; + const alertsClient = await racContext.getAlertsClient(); const { status, ids, index, query } = req.body; if (ids != null && ids.length > 1000) { diff --git a/x-pack/plugins/rule_registry/server/routes/find.ts b/x-pack/plugins/rule_registry/server/routes/find.ts index abf465fc5238..eca0a6c2a055 100644 --- a/x-pack/plugins/rule_registry/server/routes/find.ts +++ b/x-pack/plugins/rule_registry/server/routes/find.ts @@ -43,7 +43,8 @@ export const findAlertsByQueryRoute = (router: IRouter // eslint-disable-next-line @typescript-eslint/naming-convention const { query, aggs, _source, track_total_hits, size, index } = request.body; - const alertsClient = await context.rac.getAlertsClient(); + const racContext = await context.rac; + const alertsClient = await racContext.getAlertsClient(); const alerts = await alertsClient.find({ query, diff --git a/x-pack/plugins/rule_registry/server/routes/get_alert_by_id.ts b/x-pack/plugins/rule_registry/server/routes/get_alert_by_id.ts index 86e2b25850aa..2dc2d1ec2db7 100644 --- a/x-pack/plugins/rule_registry/server/routes/get_alert_by_id.ts +++ b/x-pack/plugins/rule_registry/server/routes/get_alert_by_id.ts @@ -40,7 +40,8 @@ export const getAlertByIdRoute = (router: IRouter) => }, async (context, request, response) => { try { - const alertsClient = await context.rac.getAlertsClient(); + const racContext = await context.rac; + const alertsClient = await racContext.getAlertsClient(); const { id, index } = request.query; const alert = await alertsClient.get({ id, index }); if (alert == null) { diff --git a/x-pack/plugins/rule_registry/server/routes/get_alert_index.ts b/x-pack/plugins/rule_registry/server/routes/get_alert_index.ts index 650c47f53f69..3da040de6696 100644 --- a/x-pack/plugins/rule_registry/server/routes/get_alert_index.ts +++ b/x-pack/plugins/rule_registry/server/routes/get_alert_index.ts @@ -34,7 +34,8 @@ export const getAlertsIndexRoute = (router: IRouter) = }, async (context, request, response) => { try { - const alertsClient = await context.rac.getAlertsClient(); + const racContext = await context.rac; + const alertsClient = await racContext.getAlertsClient(); const { features } = request.query; const indexName = await alertsClient.getAuthorizedAlertsIndices( features?.split(',') ?? validFeatureIds diff --git a/x-pack/plugins/rule_registry/server/routes/update_alert_by_id.ts b/x-pack/plugins/rule_registry/server/routes/update_alert_by_id.ts index 1dcf7d28380d..69f6b338bfda 100644 --- a/x-pack/plugins/rule_registry/server/routes/update_alert_by_id.ts +++ b/x-pack/plugins/rule_registry/server/routes/update_alert_by_id.ts @@ -42,7 +42,8 @@ export const updateAlertByIdRoute = (router: IRouter) }, async (context, req, response) => { try { - const alertsClient = await context.rac.getAlertsClient(); + const racContext = await context.rac; + const alertsClient = await racContext.getAlertsClient(); const { status, ids, index, _version } = req.body; const updatedAlert = await alertsClient.update({ diff --git a/x-pack/plugins/rule_registry/server/types.ts b/x-pack/plugins/rule_registry/server/types.ts index b634d352aaf8..6a7d5b849c77 100644 --- a/x-pack/plugins/rule_registry/server/types.ts +++ b/x-pack/plugins/rule_registry/server/types.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { RequestHandlerContext } from '@kbn/core/server'; +import { CustomRequestHandlerContext } from '@kbn/core/server'; import { AlertInstanceContext, AlertInstanceState, @@ -68,6 +68,6 @@ export interface RacApiRequestHandlerContext { /** * @internal */ -export interface RacRequestHandlerContext extends RequestHandlerContext { +export type RacRequestHandlerContext = CustomRequestHandlerContext<{ rac: RacApiRequestHandlerContext; -} +}>; diff --git a/x-pack/plugins/saved_objects_tagging/server/plugin.ts b/x-pack/plugins/saved_objects_tagging/server/plugin.ts index bc43d4d65c82..05a2bb31514f 100644 --- a/x-pack/plugins/saved_objects_tagging/server/plugin.ts +++ b/x-pack/plugins/saved_objects_tagging/server/plugin.ts @@ -35,7 +35,7 @@ export class SavedObjectTaggingPlugin implements Plugin<{}, {}, SetupDeps, {}> { http.registerRouteHandlerContext( 'tags', async (context, req, res) => { - return new TagsRequestHandlerContext(req, context.core, security); + return new TagsRequestHandlerContext(req, await context.core, security); } ); diff --git a/x-pack/plugins/saved_objects_tagging/server/request_handler_context.ts b/x-pack/plugins/saved_objects_tagging/server/request_handler_context.ts index 09c9f77d6957..00f0de038851 100644 --- a/x-pack/plugins/saved_objects_tagging/server/request_handler_context.ts +++ b/x-pack/plugins/saved_objects_tagging/server/request_handler_context.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { RequestHandlerContext, KibanaRequest } from '@kbn/core/server'; +import type { CoreRequestHandlerContext, KibanaRequest } from '@kbn/core/server'; import { SecurityPluginSetup } from '@kbn/security-plugin/server'; import { ITagsClient } from '../common/types'; import { ITagsRequestHandlerContext } from './types'; @@ -17,7 +17,7 @@ export class TagsRequestHandlerContext implements ITagsRequestHandlerContext { constructor( private readonly request: KibanaRequest, - private readonly coreContext: RequestHandlerContext['core'], + private readonly coreContext: CoreRequestHandlerContext, private readonly security?: SecurityPluginSetup ) {} diff --git a/x-pack/plugins/saved_objects_tagging/server/routes/assignments/find_assignable_objects.ts b/x-pack/plugins/saved_objects_tagging/server/routes/assignments/find_assignable_objects.ts index 8bda50940cc2..2fd6dfe56b33 100644 --- a/x-pack/plugins/saved_objects_tagging/server/routes/assignments/find_assignable_objects.ts +++ b/x-pack/plugins/saved_objects_tagging/server/routes/assignments/find_assignable_objects.ts @@ -22,7 +22,7 @@ export const registerFindAssignableObjectsRoute = (router: TagsPluginRouter) => }, }, router.handleLegacyErrors(async (ctx, req, res) => { - const { assignmentService } = ctx.tags!; + const { assignmentService } = await ctx.tags; const { query } = req; const results = await assignmentService.findAssignableObjects({ diff --git a/x-pack/plugins/saved_objects_tagging/server/routes/assignments/get_assignable_types.ts b/x-pack/plugins/saved_objects_tagging/server/routes/assignments/get_assignable_types.ts index 385de4eb9fb6..bd82e7d31bc7 100644 --- a/x-pack/plugins/saved_objects_tagging/server/routes/assignments/get_assignable_types.ts +++ b/x-pack/plugins/saved_objects_tagging/server/routes/assignments/get_assignable_types.ts @@ -15,7 +15,7 @@ export const registerGetAssignableTypesRoute = (router: TagsPluginRouter) => { validate: {}, }, router.handleLegacyErrors(async (ctx, req, res) => { - const { assignmentService } = ctx.tags!; + const { assignmentService } = await ctx.tags; const types = await assignmentService.getAssignableTypes(); return res.ok({ diff --git a/x-pack/plugins/saved_objects_tagging/server/routes/assignments/update_tags_assignments.ts b/x-pack/plugins/saved_objects_tagging/server/routes/assignments/update_tags_assignments.ts index c6c98ebd7b68..5bc1f844c4d3 100644 --- a/x-pack/plugins/saved_objects_tagging/server/routes/assignments/update_tags_assignments.ts +++ b/x-pack/plugins/saved_objects_tagging/server/routes/assignments/update_tags_assignments.ts @@ -37,7 +37,7 @@ export const registerUpdateTagsAssignmentsRoute = (router: TagsPluginRouter) => }, router.handleLegacyErrors(async (ctx, req, res) => { try { - const { assignmentService } = ctx.tags!; + const { assignmentService } = await ctx.tags; const { tags, assign, unassign } = req.body; await assignmentService.updateTagAssignments({ diff --git a/x-pack/plugins/saved_objects_tagging/server/routes/internal/bulk_delete.ts b/x-pack/plugins/saved_objects_tagging/server/routes/internal/bulk_delete.ts index e70819346681..ea4471f7d124 100644 --- a/x-pack/plugins/saved_objects_tagging/server/routes/internal/bulk_delete.ts +++ b/x-pack/plugins/saved_objects_tagging/server/routes/internal/bulk_delete.ts @@ -20,7 +20,7 @@ export const registerInternalBulkDeleteRoute = (router: TagsPluginRouter) => { }, router.handleLegacyErrors(async (ctx, req, res) => { const { ids: tagIds } = req.body; - const client = ctx.tags!.tagsClient; + const client = (await ctx.tags).tagsClient; for (const tagId of tagIds) { await client.delete(tagId); diff --git a/x-pack/plugins/saved_objects_tagging/server/routes/internal/find_tags.ts b/x-pack/plugins/saved_objects_tagging/server/routes/internal/find_tags.ts index 0506b9051e46..32318f39b5a0 100644 --- a/x-pack/plugins/saved_objects_tagging/server/routes/internal/find_tags.ts +++ b/x-pack/plugins/saved_objects_tagging/server/routes/internal/find_tags.ts @@ -26,7 +26,7 @@ export const registerInternalFindTagsRoute = (router: TagsPluginRouter) => { }, router.handleLegacyErrors(async (ctx, req, res) => { const { query } = req; - const { client, typeRegistry } = ctx.core.savedObjects; + const { client, typeRegistry } = (await ctx.core).savedObjects; const findResponse = await client.find({ page: query.page, diff --git a/x-pack/plugins/saved_objects_tagging/server/routes/tags/create_tag.ts b/x-pack/plugins/saved_objects_tagging/server/routes/tags/create_tag.ts index 0001ff422a2b..7b9e6a32d3ae 100644 --- a/x-pack/plugins/saved_objects_tagging/server/routes/tags/create_tag.ts +++ b/x-pack/plugins/saved_objects_tagging/server/routes/tags/create_tag.ts @@ -23,7 +23,8 @@ export const registerCreateTagRoute = (router: TagsPluginRouter) => { }, router.handleLegacyErrors(async (ctx, req, res) => { try { - const tag = await ctx.tags!.tagsClient.create(req.body); + const { tagsClient } = await ctx.tags; + const tag = await tagsClient.create(req.body); return res.ok({ body: { tag, diff --git a/x-pack/plugins/saved_objects_tagging/server/routes/tags/delete_tag.ts b/x-pack/plugins/saved_objects_tagging/server/routes/tags/delete_tag.ts index 42077eb05d13..505ecfd4974a 100644 --- a/x-pack/plugins/saved_objects_tagging/server/routes/tags/delete_tag.ts +++ b/x-pack/plugins/saved_objects_tagging/server/routes/tags/delete_tag.ts @@ -20,7 +20,8 @@ export const registerDeleteTagRoute = (router: TagsPluginRouter) => { }, router.handleLegacyErrors(async (ctx, req, res) => { const { id } = req.params; - await ctx.tags!.tagsClient.delete(id); + const { tagsClient } = await ctx.tags; + await tagsClient.delete(id); return res.ok(); }) ); diff --git a/x-pack/plugins/saved_objects_tagging/server/routes/tags/get_all_tags.ts b/x-pack/plugins/saved_objects_tagging/server/routes/tags/get_all_tags.ts index af3b39f26f2a..011d764983fa 100644 --- a/x-pack/plugins/saved_objects_tagging/server/routes/tags/get_all_tags.ts +++ b/x-pack/plugins/saved_objects_tagging/server/routes/tags/get_all_tags.ts @@ -14,7 +14,8 @@ export const registerGetAllTagsRoute = (router: TagsPluginRouter) => { validate: {}, }, router.handleLegacyErrors(async (ctx, req, res) => { - const tags = await ctx.tags!.tagsClient.getAll(); + const { tagsClient } = await ctx.tags; + const tags = await tagsClient.getAll(); return res.ok({ body: { tags, diff --git a/x-pack/plugins/saved_objects_tagging/server/routes/tags/get_tag.ts b/x-pack/plugins/saved_objects_tagging/server/routes/tags/get_tag.ts index 8080b6ea22a6..4488d4dae6e2 100644 --- a/x-pack/plugins/saved_objects_tagging/server/routes/tags/get_tag.ts +++ b/x-pack/plugins/saved_objects_tagging/server/routes/tags/get_tag.ts @@ -20,7 +20,8 @@ export const registerGetTagRoute = (router: TagsPluginRouter) => { }, router.handleLegacyErrors(async (ctx, req, res) => { const { id } = req.params; - const tag = await ctx.tags!.tagsClient.get(id); + const { tagsClient } = await ctx.tags; + const tag = await tagsClient.get(id); return res.ok({ body: { tag, diff --git a/x-pack/plugins/saved_objects_tagging/server/routes/tags/update_tag.ts b/x-pack/plugins/saved_objects_tagging/server/routes/tags/update_tag.ts index d357c42d1924..67b7d7a6acbd 100644 --- a/x-pack/plugins/saved_objects_tagging/server/routes/tags/update_tag.ts +++ b/x-pack/plugins/saved_objects_tagging/server/routes/tags/update_tag.ts @@ -27,7 +27,8 @@ export const registerUpdateTagRoute = (router: TagsPluginRouter) => { router.handleLegacyErrors(async (ctx, req, res) => { const { id } = req.params; try { - const tag = await ctx.tags!.tagsClient.update(id, req.body); + const { tagsClient } = await ctx.tags; + const tag = await tagsClient.update(id, req.body); return res.ok({ body: { tag, diff --git a/x-pack/plugins/saved_objects_tagging/server/types.ts b/x-pack/plugins/saved_objects_tagging/server/types.ts index 92618c7d3fb5..366f2779b6a8 100644 --- a/x-pack/plugins/saved_objects_tagging/server/types.ts +++ b/x-pack/plugins/saved_objects_tagging/server/types.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { IRouter, RequestHandlerContext } from '@kbn/core/server'; +import type { IRouter, CustomRequestHandlerContext } from '@kbn/core/server'; import { ITagsClient } from '../common/types'; import { IAssignmentService } from './services'; @@ -17,9 +17,9 @@ export interface ITagsRequestHandlerContext { /** * @internal */ -export interface TagsHandlerContext extends RequestHandlerContext { +export type TagsHandlerContext = CustomRequestHandlerContext<{ tags: ITagsRequestHandlerContext; -} +}>; /** * @internal diff --git a/x-pack/plugins/searchprofiler/server/routes/profile.ts b/x-pack/plugins/searchprofiler/server/routes/profile.ts index 22c97e240a3b..9e76bf0df96a 100644 --- a/x-pack/plugins/searchprofiler/server/routes/profile.ts +++ b/x-pack/plugins/searchprofiler/server/routes/profile.ts @@ -43,7 +43,7 @@ export const register = ({ router, getLicenseStatus, log }: RouteDependencies) = }; try { - const client = ctx.core.elasticsearch.client.asCurrentUser; + const client = (await ctx.core).elasticsearch.client.asCurrentUser; const resp = await client.search(body); return response.ok({ diff --git a/x-pack/plugins/security/server/routes/api_keys/create.test.ts b/x-pack/plugins/security/server/routes/api_keys/create.test.ts index e88b4f0537d0..dbf4c93639e4 100644 --- a/x-pack/plugins/security/server/routes/api_keys/create.test.ts +++ b/x-pack/plugins/security/server/routes/api_keys/create.test.ts @@ -9,12 +9,11 @@ import Boom from '@hapi/boom'; import type { RequestHandler } from '@kbn/core/server'; import { kibanaResponseFactory } from '@kbn/core/server'; -import { httpServerMock } from '@kbn/core/server/mocks'; +import { coreMock, httpServerMock } from '@kbn/core/server/mocks'; import type { DeeplyMockedKeys } from '@kbn/utility-types/jest'; import type { InternalAuthenticationServiceStart } from '../../authentication'; import { authenticationServiceMock } from '../../authentication/authentication_service.mock'; -import type { SecurityRequestHandlerContext } from '../../types'; import { routeDefinitionParamsMock } from '../index.mock'; import { defineCreateApiKeyRoutes } from './create'; @@ -22,9 +21,9 @@ describe('Create API Key route', () => { function getMockContext( licenseCheckResult: { state: string; message?: string } = { state: 'valid' } ) { - return { + return coreMock.createCustomRequestHandlerContext({ licensing: { license: { check: jest.fn().mockReturnValue(licenseCheckResult) } }, - } as unknown as SecurityRequestHandlerContext; + }); } let routeHandler: RequestHandler; @@ -53,7 +52,7 @@ describe('Create API Key route', () => { expect(response.status).toBe(403); expect(response.payload).toEqual({ message: 'test forbidden message' }); - expect(mockContext.licensing.license.check).toHaveBeenCalledWith('security', 'basic'); + expect((await mockContext.licensing).license.check).toHaveBeenCalledWith('security', 'basic'); }); test('returns error from cluster client', async () => { diff --git a/x-pack/plugins/security/server/routes/api_keys/enabled.test.ts b/x-pack/plugins/security/server/routes/api_keys/enabled.test.ts index d768a421316f..7beef71256c4 100644 --- a/x-pack/plugins/security/server/routes/api_keys/enabled.test.ts +++ b/x-pack/plugins/security/server/routes/api_keys/enabled.test.ts @@ -9,12 +9,11 @@ import Boom from '@hapi/boom'; import type { RequestHandler } from '@kbn/core/server'; import { kibanaResponseFactory } from '@kbn/core/server'; -import { httpServerMock } from '@kbn/core/server/mocks'; +import { coreMock, httpServerMock } from '@kbn/core/server/mocks'; import type { DeeplyMockedKeys } from '@kbn/utility-types/jest'; import type { InternalAuthenticationServiceStart } from '../../authentication'; import { authenticationServiceMock } from '../../authentication/authentication_service.mock'; -import type { SecurityRequestHandlerContext } from '../../types'; import { routeDefinitionParamsMock } from '../index.mock'; import { defineEnabledApiKeysRoutes } from './enabled'; @@ -22,9 +21,9 @@ describe('API keys enabled', () => { function getMockContext( licenseCheckResult: { state: string; message?: string } = { state: 'valid' } ) { - return { + return coreMock.createCustomRequestHandlerContext({ licensing: { license: { check: jest.fn().mockReturnValue(licenseCheckResult) } }, - } as unknown as SecurityRequestHandlerContext; + }); } let routeHandler: RequestHandler; @@ -53,7 +52,7 @@ describe('API keys enabled', () => { expect(response.status).toBe(403); expect(response.payload).toEqual({ message: 'test forbidden message' }); - expect(mockContext.licensing.license.check).toHaveBeenCalledWith('security', 'basic'); + expect((await mockContext.licensing).license.check).toHaveBeenCalledWith('security', 'basic'); }); test('returns error from cluster client', async () => { diff --git a/x-pack/plugins/security/server/routes/api_keys/get.test.ts b/x-pack/plugins/security/server/routes/api_keys/get.test.ts index f05f4d5c3733..c34a67ff1bfc 100644 --- a/x-pack/plugins/security/server/routes/api_keys/get.test.ts +++ b/x-pack/plugins/security/server/routes/api_keys/get.test.ts @@ -28,13 +28,17 @@ describe('Get API keys', () => { ) => { test(description, async () => { const mockRouteDefinitionParams = routeDefinitionParamsMock.create(); - const mockContext = { - core: coreMock.createRequestHandlerContext(), - licensing: { license: { check: jest.fn().mockReturnValue(licenseCheckResult) } } as any, + const mockCoreContext = coreMock.createRequestHandlerContext(); + const mockLicensingContext = { + license: { check: jest.fn().mockReturnValue(licenseCheckResult) }, }; + const mockContext = coreMock.createCustomRequestHandlerContext({ + core: mockCoreContext, + licensing: mockLicensingContext as any, + }); if (apiResponse) { - mockContext.core.elasticsearch.client.asCurrentUser.security.getApiKey.mockResponse( + mockCoreContext.elasticsearch.client.asCurrentUser.security.getApiKey.mockResponse( // @ts-expect-error unknown type apiResponse() ); @@ -57,10 +61,10 @@ describe('Get API keys', () => { if (apiResponse) { expect( - mockContext.core.elasticsearch.client.asCurrentUser.security.getApiKey + mockCoreContext.elasticsearch.client.asCurrentUser.security.getApiKey ).toHaveBeenCalledWith({ owner: !isAdmin }); } - expect(mockContext.licensing.license.check).toHaveBeenCalledWith('security', 'basic'); + expect(mockLicensingContext.license.check).toHaveBeenCalledWith('security', 'basic'); }); }; diff --git a/x-pack/plugins/security/server/routes/api_keys/get.ts b/x-pack/plugins/security/server/routes/api_keys/get.ts index 5907dacb21a9..db0d02a20c68 100644 --- a/x-pack/plugins/security/server/routes/api_keys/get.ts +++ b/x-pack/plugins/security/server/routes/api_keys/get.ts @@ -29,10 +29,10 @@ export function defineGetApiKeysRoutes({ router }: RouteDefinitionParams) { createLicensedRouteHandler(async (context, request, response) => { try { const isAdmin = request.query.isAdmin === 'true'; - const apiResponse = - await context.core.elasticsearch.client.asCurrentUser.security.getApiKey({ - owner: !isAdmin, - }); + const esClient = (await context.core).elasticsearch.client; + const apiResponse = await esClient.asCurrentUser.security.getApiKey({ + owner: !isAdmin, + }); const validKeys = apiResponse.api_keys.filter(({ invalidated }) => !invalidated); diff --git a/x-pack/plugins/security/server/routes/api_keys/invalidate.test.ts b/x-pack/plugins/security/server/routes/api_keys/invalidate.test.ts index 386e894eb335..c6f4594e8fb3 100644 --- a/x-pack/plugins/security/server/routes/api_keys/invalidate.test.ts +++ b/x-pack/plugins/security/server/routes/api_keys/invalidate.test.ts @@ -29,13 +29,17 @@ describe('Invalidate API keys', () => { ) => { test(description, async () => { const mockRouteDefinitionParams = routeDefinitionParamsMock.create(); - const mockContext = { - core: coreMock.createRequestHandlerContext(), - licensing: { license: { check: jest.fn().mockReturnValue(licenseCheckResult) } } as any, - }; + const mockCoreContext = coreMock.createRequestHandlerContext(); + const mockLicensingContext = { + license: { check: jest.fn().mockReturnValue(licenseCheckResult) }, + } as any; + const mockContext = coreMock.createCustomRequestHandlerContext({ + core: mockCoreContext, + licensing: mockLicensingContext, + }); for (const apiResponse of apiResponses) { - mockContext.core.elasticsearch.client.asCurrentUser.security.invalidateApiKey.mockImplementationOnce( + mockCoreContext.elasticsearch.client.asCurrentUser.security.invalidateApiKey.mockImplementationOnce( (async () => ({ body: await apiResponse() })) as any ); } @@ -58,11 +62,11 @@ describe('Invalidate API keys', () => { if (Array.isArray(asserts.apiArguments)) { for (const apiArguments of asserts.apiArguments) { expect( - mockContext.core.elasticsearch.client.asCurrentUser.security.invalidateApiKey + mockCoreContext.elasticsearch.client.asCurrentUser.security.invalidateApiKey ).toHaveBeenCalledWith(apiArguments); } } - expect(mockContext.licensing.license.check).toHaveBeenCalledWith('security', 'basic'); + expect(mockLicensingContext.license.check).toHaveBeenCalledWith('security', 'basic'); }); }; diff --git a/x-pack/plugins/security/server/routes/api_keys/invalidate.ts b/x-pack/plugins/security/server/routes/api_keys/invalidate.ts index 179df26a874b..58f25bbf80b4 100644 --- a/x-pack/plugins/security/server/routes/api_keys/invalidate.ts +++ b/x-pack/plugins/security/server/routes/api_keys/invalidate.ts @@ -35,13 +35,14 @@ export function defineInvalidateApiKeysRoutes({ router }: RouteDefinitionParams) await Promise.all( request.body.apiKeys.map(async (key) => { try { + const esClient = (await context.core).elasticsearch.client; const body: { ids: string[]; owner?: boolean } = { ids: [key.id] }; if (!request.body.isAdmin) { body.owner = true; } // Send the request to invalidate the API key and return an error if it could not be deleted. - await context.core.elasticsearch.client.asCurrentUser.security.invalidateApiKey({ + await esClient.asCurrentUser.security.invalidateApiKey({ body, }); return { key, error: undefined }; diff --git a/x-pack/plugins/security/server/routes/api_keys/privileges.test.ts b/x-pack/plugins/security/server/routes/api_keys/privileges.test.ts index 06e978e94758..52d1d59a486d 100644 --- a/x-pack/plugins/security/server/routes/api_keys/privileges.test.ts +++ b/x-pack/plugins/security/server/routes/api_keys/privileges.test.ts @@ -34,17 +34,21 @@ describe('Check API keys privileges', () => { ) => { test(description, async () => { const mockRouteDefinitionParams = routeDefinitionParamsMock.create(); - const mockContext = { - core: coreMock.createRequestHandlerContext(), - licensing: { license: { check: jest.fn().mockReturnValue(licenseCheckResult) } } as any, - }; + const mockCoreContext = coreMock.createRequestHandlerContext(); + const mockLicensingContext = { + license: { check: jest.fn().mockReturnValue(licenseCheckResult) }, + } as any; + const mockContext = coreMock.createCustomRequestHandlerContext({ + core: mockCoreContext, + licensing: mockLicensingContext, + }); const authc = authenticationServiceMock.createStart(); authc.apiKeys.areAPIKeysEnabled.mockResolvedValue(areAPIKeysEnabled); mockRouteDefinitionParams.getAuthenticationService.mockReturnValue(authc); if (apiResponse) { - mockContext.core.elasticsearch.client.asCurrentUser.security.hasPrivileges.mockResponseImplementation( + mockCoreContext.elasticsearch.client.asCurrentUser.security.hasPrivileges.mockResponseImplementation( // @ts-expect-error unknown return () => { return { @@ -70,11 +74,11 @@ describe('Check API keys privileges', () => { if (asserts.apiArguments) { expect( - mockContext.core.elasticsearch.client.asCurrentUser.security.hasPrivileges + mockCoreContext.elasticsearch.client.asCurrentUser.security.hasPrivileges ).toHaveBeenCalledWith(asserts.apiArguments); } - expect(mockContext.licensing.license.check).toHaveBeenCalledWith('security', 'basic'); + expect(mockLicensingContext.license.check).toHaveBeenCalledWith('security', 'basic'); }); }; diff --git a/x-pack/plugins/security/server/routes/api_keys/privileges.ts b/x-pack/plugins/security/server/routes/api_keys/privileges.ts index 7fa92e21ddba..09210e1d00d3 100644 --- a/x-pack/plugins/security/server/routes/api_keys/privileges.ts +++ b/x-pack/plugins/security/server/routes/api_keys/privileges.ts @@ -20,6 +20,7 @@ export function defineCheckPrivilegesRoutes({ }, createLicensedRouteHandler(async (context, request, response) => { try { + const esClient = (await context.core).elasticsearch.client; const [ { cluster: { @@ -30,7 +31,7 @@ export function defineCheckPrivilegesRoutes({ }, areApiKeysEnabled, ] = await Promise.all([ - context.core.elasticsearch.client.asCurrentUser.security.hasPrivileges({ + esClient.asCurrentUser.security.hasPrivileges({ body: { cluster: ['manage_security', 'manage_api_key', 'manage_own_api_key'] }, }), getAuthenticationService().apiKeys.areAPIKeysEnabled(), diff --git a/x-pack/plugins/security/server/routes/authorization/privileges/get.test.ts b/x-pack/plugins/security/server/routes/authorization/privileges/get.test.ts index 30a740cc3a98..fca0a0e4388f 100644 --- a/x-pack/plugins/security/server/routes/authorization/privileges/get.test.ts +++ b/x-pack/plugins/security/server/routes/authorization/privileges/get.test.ts @@ -6,7 +6,7 @@ */ import { kibanaResponseFactory } from '@kbn/core/server'; -import { httpServerMock } from '@kbn/core/server/mocks'; +import { coreMock, httpServerMock } from '@kbn/core/server/mocks'; import type { LicenseCheck } from '@kbn/licensing-plugin/server'; import type { RawKibanaPrivileges } from '../../../../common/model'; @@ -66,15 +66,18 @@ describe('GET privileges', () => { query: includeActions ? { includeActions: 'true' } : undefined, headers, }); - const mockContext = { - licensing: { license: { check: jest.fn().mockReturnValue(licenseCheckResult) } }, - } as unknown as SecurityRequestHandlerContext; + const mockLicensingContext = { + license: { check: jest.fn().mockReturnValue(licenseCheckResult) }, + }; + const mockContext = coreMock.createCustomRequestHandlerContext({ + licensing: mockLicensingContext, + }) as unknown as SecurityRequestHandlerContext; const response = await handler(mockContext, mockRequest, kibanaResponseFactory); expect(response.status).toBe(asserts.statusCode); expect(response.payload).toEqual(asserts.result); - expect(mockContext.licensing.license.check).toHaveBeenCalledWith('security', 'basic'); + expect(mockLicensingContext.license.check).toHaveBeenCalledWith('security', 'basic'); }); }; diff --git a/x-pack/plugins/security/server/routes/authorization/privileges/get_builtin.ts b/x-pack/plugins/security/server/routes/authorization/privileges/get_builtin.ts index 40b67aa08b07..e51e8e1d5ae5 100644 --- a/x-pack/plugins/security/server/routes/authorization/privileges/get_builtin.ts +++ b/x-pack/plugins/security/server/routes/authorization/privileges/get_builtin.ts @@ -11,8 +11,8 @@ export function defineGetBuiltinPrivilegesRoutes({ router }: RouteDefinitionPara router.get( { path: '/internal/security/esPrivileges/builtin', validate: false }, async (context, request, response) => { - const privileges = - await context.core.elasticsearch.client.asCurrentUser.security.getBuiltinPrivileges(); + const esClient = (await context.core).elasticsearch.client; + const privileges = await esClient.asCurrentUser.security.getBuiltinPrivileges(); // Exclude the `none` privilege, as it doesn't make sense as an option within the Kibana UI privileges.cluster = privileges.cluster.filter((privilege) => privilege !== 'none'); diff --git a/x-pack/plugins/security/server/routes/authorization/roles/delete.test.ts b/x-pack/plugins/security/server/routes/authorization/roles/delete.test.ts index 3f5a17fd3858..dfe2af12a23e 100644 --- a/x-pack/plugins/security/server/routes/authorization/roles/delete.test.ts +++ b/x-pack/plugins/security/server/routes/authorization/roles/delete.test.ts @@ -28,13 +28,17 @@ describe('DELETE role', () => { ) => { test(description, async () => { const mockRouteDefinitionParams = routeDefinitionParamsMock.create(); - const mockContext = { - core: coreMock.createRequestHandlerContext(), - licensing: { license: { check: jest.fn().mockReturnValue(licenseCheckResult) } } as any, - }; + const mockCoreContext = coreMock.createRequestHandlerContext(); + const mockLicensingContext = { + license: { check: jest.fn().mockReturnValue(licenseCheckResult) }, + } as any; + const mockContext = coreMock.createCustomRequestHandlerContext({ + core: mockCoreContext, + licensing: mockLicensingContext, + }); if (apiResponse) { - mockContext.core.elasticsearch.client.asCurrentUser.security.deleteRole.mockImplementation( + mockCoreContext.elasticsearch.client.asCurrentUser.security.deleteRole.mockImplementation( (async () => ({ body: await apiResponse() })) as any ); } @@ -56,10 +60,10 @@ describe('DELETE role', () => { if (apiResponse) { expect( - mockContext.core.elasticsearch.client.asCurrentUser.security.deleteRole + mockCoreContext.elasticsearch.client.asCurrentUser.security.deleteRole ).toHaveBeenCalledWith({ name }); } - expect(mockContext.licensing.license.check).toHaveBeenCalledWith('security', 'basic'); + expect(mockLicensingContext.license.check).toHaveBeenCalledWith('security', 'basic'); }); }; diff --git a/x-pack/plugins/security/server/routes/authorization/roles/delete.ts b/x-pack/plugins/security/server/routes/authorization/roles/delete.ts index e30221b2e9e9..b484fd015184 100644 --- a/x-pack/plugins/security/server/routes/authorization/roles/delete.ts +++ b/x-pack/plugins/security/server/routes/authorization/roles/delete.ts @@ -21,7 +21,8 @@ export function defineDeleteRolesRoutes({ router }: RouteDefinitionParams) { }, createLicensedRouteHandler(async (context, request, response) => { try { - await context.core.elasticsearch.client.asCurrentUser.security.deleteRole({ + const esClient = (await context.core).elasticsearch.client; + await esClient.asCurrentUser.security.deleteRole({ name: request.params.name, }); diff --git a/x-pack/plugins/security/server/routes/authorization/roles/get.test.ts b/x-pack/plugins/security/server/routes/authorization/roles/get.test.ts index 1a7e56e15755..90457c71b90a 100644 --- a/x-pack/plugins/security/server/routes/authorization/roles/get.test.ts +++ b/x-pack/plugins/security/server/routes/authorization/roles/get.test.ts @@ -34,13 +34,17 @@ describe('GET role', () => { mockRouteDefinitionParams.authz.applicationName = application; mockRouteDefinitionParams.getFeatures = jest.fn().mockResolvedValue([]); - const mockContext = { - core: coreMock.createRequestHandlerContext(), - licensing: { license: { check: jest.fn().mockReturnValue(licenseCheckResult) } } as any, - }; + const mockCoreContext = coreMock.createRequestHandlerContext(); + const mockLicensingContext = { + license: { check: jest.fn().mockReturnValue(licenseCheckResult) }, + } as any; + const mockContext = coreMock.createCustomRequestHandlerContext({ + core: mockCoreContext, + licensing: mockLicensingContext, + }); if (apiResponse) { - mockContext.core.elasticsearch.client.asCurrentUser.security.getRole.mockResponseImplementation( + mockCoreContext.elasticsearch.client.asCurrentUser.security.getRole.mockResponseImplementation( (() => ({ body: apiResponse() })) as any ); } @@ -62,11 +66,11 @@ describe('GET role', () => { if (apiResponse) { expect( - mockContext.core.elasticsearch.client.asCurrentUser.security.getRole + mockCoreContext.elasticsearch.client.asCurrentUser.security.getRole ).toHaveBeenCalledWith({ name }); } - expect(mockContext.licensing.license.check).toHaveBeenCalledWith('security', 'basic'); + expect(mockLicensingContext.license.check).toHaveBeenCalledWith('security', 'basic'); }); }; diff --git a/x-pack/plugins/security/server/routes/authorization/roles/get.ts b/x-pack/plugins/security/server/routes/authorization/roles/get.ts index 125966d887e0..9ae385b1ab3f 100644 --- a/x-pack/plugins/security/server/routes/authorization/roles/get.ts +++ b/x-pack/plugins/security/server/routes/authorization/roles/get.ts @@ -22,9 +22,10 @@ export function defineGetRolesRoutes({ router, authz, getFeatures }: RouteDefini }, createLicensedRouteHandler(async (context, request, response) => { try { + const esClient = (await context.core).elasticsearch.client; const [features, elasticsearchRoles] = await Promise.all([ getFeatures(), - await context.core.elasticsearch.client.asCurrentUser.security.getRole({ + await esClient.asCurrentUser.security.getRole({ name: request.params.name, }), ]); diff --git a/x-pack/plugins/security/server/routes/authorization/roles/get_all.test.ts b/x-pack/plugins/security/server/routes/authorization/roles/get_all.test.ts index 3c7c8a0ebf6b..a7833f803afa 100644 --- a/x-pack/plugins/security/server/routes/authorization/roles/get_all.test.ts +++ b/x-pack/plugins/security/server/routes/authorization/roles/get_all.test.ts @@ -34,13 +34,17 @@ describe('GET all roles', () => { mockRouteDefinitionParams.authz.applicationName = application; mockRouteDefinitionParams.getFeatures = jest.fn().mockResolvedValue([]); - const mockContext = { - core: coreMock.createRequestHandlerContext(), - licensing: { license: { check: jest.fn().mockReturnValue(licenseCheckResult) } } as any, - }; + const mockCoreContext = coreMock.createRequestHandlerContext(); + const mockLicensingContext = { + license: { check: jest.fn().mockReturnValue(licenseCheckResult) }, + } as any; + const mockContext = coreMock.createCustomRequestHandlerContext({ + core: mockCoreContext, + licensing: mockLicensingContext, + }); if (apiResponse) { - mockContext.core.elasticsearch.client.asCurrentUser.security.getRole.mockResponseImplementation( + mockCoreContext.elasticsearch.client.asCurrentUser.security.getRole.mockResponseImplementation( (() => ({ body: apiResponse() })) as any ); } @@ -61,10 +65,10 @@ describe('GET all roles', () => { if (apiResponse) { expect( - mockContext.core.elasticsearch.client.asCurrentUser.security.getRole + mockCoreContext.elasticsearch.client.asCurrentUser.security.getRole ).toHaveBeenCalled(); } - expect(mockContext.licensing.license.check).toHaveBeenCalledWith('security', 'basic'); + expect(mockLicensingContext.license.check).toHaveBeenCalledWith('security', 'basic'); }); }; diff --git a/x-pack/plugins/security/server/routes/authorization/roles/get_all.ts b/x-pack/plugins/security/server/routes/authorization/roles/get_all.ts index f34b6a089366..20f967db598f 100644 --- a/x-pack/plugins/security/server/routes/authorization/roles/get_all.ts +++ b/x-pack/plugins/security/server/routes/authorization/roles/get_all.ts @@ -15,9 +15,10 @@ export function defineGetAllRolesRoutes({ router, authz, getFeatures }: RouteDef { path: '/api/security/role', validate: false }, createLicensedRouteHandler(async (context, request, response) => { try { + const esClient = (await context.core).elasticsearch.client; const [features, elasticsearchRoles] = await Promise.all([ getFeatures(), - await context.core.elasticsearch.client.asCurrentUser.security.getRole(), + await esClient.asCurrentUser.security.getRole(), ]); // Transform elasticsearch roles into Kibana roles and return in a list sorted by the role name. diff --git a/x-pack/plugins/security/server/routes/authorization/roles/put.test.ts b/x-pack/plugins/security/server/routes/authorization/roles/put.test.ts index 425115da1e20..9abef4a5e85c 100644 --- a/x-pack/plugins/security/server/routes/authorization/roles/put.test.ts +++ b/x-pack/plugins/security/server/routes/authorization/roles/put.test.ts @@ -75,19 +75,23 @@ const putRoleTest = ( mockRouteDefinitionParams.authz.applicationName = application; mockRouteDefinitionParams.authz.privileges.get.mockReturnValue(privilegeMap); - const mockContext = { - core: coreMock.createRequestHandlerContext(), - licensing: { license: { check: jest.fn().mockReturnValue(licenseCheckResult) } } as any, - }; + const mockCoreContext = coreMock.createRequestHandlerContext(); + const mockLicensingContext = { + license: { check: jest.fn().mockReturnValue(licenseCheckResult) }, + } as any; + const mockContext = coreMock.createCustomRequestHandlerContext({ + core: mockCoreContext, + licensing: mockLicensingContext, + }); if (apiResponses?.get) { - mockContext.core.elasticsearch.client.asCurrentUser.security.getRole.mockResponseImplementationOnce( + mockCoreContext.elasticsearch.client.asCurrentUser.security.getRole.mockResponseImplementationOnce( (() => ({ body: apiResponses?.get() })) as any ); } if (apiResponses?.put) { - mockContext.core.elasticsearch.client.asCurrentUser.security.putRole.mockResponseImplementationOnce( + mockCoreContext.elasticsearch.client.asCurrentUser.security.putRole.mockResponseImplementationOnce( (() => ({ body: apiResponses?.put() })) as any ); } @@ -154,15 +158,15 @@ const putRoleTest = ( if (asserts.apiArguments?.get) { expect( - mockContext.core.elasticsearch.client.asCurrentUser.security.getRole + mockCoreContext.elasticsearch.client.asCurrentUser.security.getRole ).toHaveBeenCalledWith(...asserts.apiArguments?.get); } if (asserts.apiArguments?.put) { expect( - mockContext.core.elasticsearch.client.asCurrentUser.security.putRole + mockCoreContext.elasticsearch.client.asCurrentUser.security.putRole ).toHaveBeenCalledWith(...asserts.apiArguments?.put); } - expect(mockContext.licensing.license.check).toHaveBeenCalledWith('security', 'basic'); + expect(mockLicensingContext.license.check).toHaveBeenCalledWith('security', 'basic'); if (asserts.recordSubFeaturePrivilegeUsage) { expect( diff --git a/x-pack/plugins/security/server/routes/authorization/roles/put.ts b/x-pack/plugins/security/server/routes/authorization/roles/put.ts index 60e24dcfec1c..03fbdf30dc76 100644 --- a/x-pack/plugins/security/server/routes/authorization/roles/put.ts +++ b/x-pack/plugins/security/server/routes/authorization/roles/put.ts @@ -66,12 +66,10 @@ export function definePutRolesRoutes({ const { name } = request.params; try { + const esClient = (await context.core).elasticsearch.client; const [features, rawRoles] = await Promise.all([ getFeatures(), - context.core.elasticsearch.client.asCurrentUser.security.getRole( - { name: request.params.name }, - { ignore: [404] } - ), + esClient.asCurrentUser.security.getRole({ name: request.params.name }, { ignore: [404] }), ]); const { validationErrors } = validateKibanaPrivileges(features, request.body.kibana); @@ -91,7 +89,7 @@ export function definePutRolesRoutes({ rawRoles[name] ? rawRoles[name].applications : [] ); - await context.core.elasticsearch.client.asCurrentUser.security.putRole({ + await esClient.asCurrentUser.security.putRole({ name: request.params.name, // @ts-expect-error RoleIndexPrivilege is not compatible. grant is required in IndicesPrivileges.field_security body, diff --git a/x-pack/plugins/security/server/routes/deprecations/kibana_user_role.ts b/x-pack/plugins/security/server/routes/deprecations/kibana_user_role.ts index bb07ece2c75b..036d1f141f99 100644 --- a/x-pack/plugins/security/server/routes/deprecations/kibana_user_role.ts +++ b/x-pack/plugins/security/server/routes/deprecations/kibana_user_role.ts @@ -27,8 +27,9 @@ export function defineKibanaUserRoleDeprecationRoutes({ router, logger }: RouteD }, createLicensedRouteHandler(async (context, request, response) => { let users: estypes.SecurityGetUserResponse; + const esClient = (await context.core).elasticsearch.client; try { - users = await context.core.elasticsearch.client.asCurrentUser.security.getUser(); + users = await esClient.asCurrentUser.security.getUser(); } catch (err) { if (getErrorStatusCode(err) === 403) { logger.warn( @@ -65,7 +66,7 @@ export function defineKibanaUserRoleDeprecationRoutes({ router, logger }: RouteD } try { - await context.core.elasticsearch.client.asCurrentUser.security.putUser({ + await esClient.asCurrentUser.security.putUser({ username: userToUpdate.username, body: { ...userToUpdate, roles }, }); @@ -89,10 +90,10 @@ export function defineKibanaUserRoleDeprecationRoutes({ router, logger }: RouteD validate: false, }, createLicensedRouteHandler(async (context, request, response) => { + const esClient = (await context.core).elasticsearch.client; let roleMappings: estypes.SecurityGetRoleMappingResponse; try { - roleMappings = - await context.core.elasticsearch.client.asCurrentUser.security.getRoleMapping(); + roleMappings = await esClient.asCurrentUser.security.getRoleMapping(); } catch (err) { logger.error(`Failed to retrieve role mappings: ${getDetailedErrorMessage(err)}.`); return response.customError(wrapIntoCustomErrorResponse(err)); @@ -119,7 +120,7 @@ export function defineKibanaUserRoleDeprecationRoutes({ router, logger }: RouteD } try { - await context.core.elasticsearch.client.asCurrentUser.security.putRoleMapping({ + await esClient.asCurrentUser.security.putRoleMapping({ name: mappingNameToUpdate, body: { ...mappingToUpdate, roles }, }); diff --git a/x-pack/plugins/security/server/routes/index.mock.ts b/x-pack/plugins/security/server/routes/index.mock.ts index 939ed36ab04d..3cda2a0ec9bc 100644 --- a/x-pack/plugins/security/server/routes/index.mock.ts +++ b/x-pack/plugins/security/server/routes/index.mock.ts @@ -51,8 +51,9 @@ export const routeDefinitionParamsMock = { }; export const securityRequestHandlerContextMock = { - create: (): SecurityRequestHandlerContext => ({ - core: coreMock.createRequestHandlerContext(), - licensing: licensingMock.createRequestHandlerContext(), - }), + create: (): SecurityRequestHandlerContext => + coreMock.createCustomRequestHandlerContext({ + core: coreMock.createRequestHandlerContext(), + licensing: licensingMock.createRequestHandlerContext(), + }), }; diff --git a/x-pack/plugins/security/server/routes/indices/get_fields.ts b/x-pack/plugins/security/server/routes/indices/get_fields.ts index da1a74256005..b0ec51339e08 100644 --- a/x-pack/plugins/security/server/routes/indices/get_fields.ts +++ b/x-pack/plugins/security/server/routes/indices/get_fields.ts @@ -18,14 +18,14 @@ export function defineGetFieldsRoutes({ router }: RouteDefinitionParams) { }, async (context, request, response) => { try { - const indexMappings = - await context.core.elasticsearch.client.asCurrentUser.indices.getFieldMapping({ - index: request.params.query, - fields: '*', - allow_no_indices: false, - include_defaults: true, - filter_path: '*.mappings.*.mapping.*.type', - }); + const esClient = (await context.core).elasticsearch.client; + const indexMappings = await esClient.asCurrentUser.indices.getFieldMapping({ + index: request.params.query, + fields: '*', + allow_no_indices: false, + include_defaults: true, + filter_path: '*.mappings.*.mapping.*.type', + }); // The flow is the following (see response format at https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-get-field-mapping.html): // 1. Iterate over all matched indices. diff --git a/x-pack/plugins/security/server/routes/licensed_route_handler.ts b/x-pack/plugins/security/server/routes/licensed_route_handler.ts index cf631c64c4a3..59cb6150cd4c 100644 --- a/x-pack/plugins/security/server/routes/licensed_route_handler.ts +++ b/x-pack/plugins/security/server/routes/licensed_route_handler.ts @@ -19,12 +19,12 @@ export const createLicensedRouteHandler = < >( handler: RequestHandler ) => { - const licensedRouteHandler: RequestHandler = ( + const licensedRouteHandler: RequestHandler = async ( context, request, responseToolkit ) => { - const { license } = context.licensing; + const { license } = await context.licensing; const licenseCheck = license.check('security', 'basic'); if (licenseCheck.state === 'unavailable' || licenseCheck.state === 'invalid') { return responseToolkit.forbidden({ body: { message: licenseCheck.message! } }); diff --git a/x-pack/plugins/security/server/routes/role_mapping/delete.test.ts b/x-pack/plugins/security/server/routes/role_mapping/delete.test.ts index 9a63c16a6ef4..63e0d602cf12 100644 --- a/x-pack/plugins/security/server/routes/role_mapping/delete.test.ts +++ b/x-pack/plugins/security/server/routes/role_mapping/delete.test.ts @@ -14,11 +14,16 @@ import { defineRoleMappingDeleteRoutes } from './delete'; describe('DELETE role mappings', () => { it('allows a role mapping to be deleted', async () => { const mockRouteDefinitionParams = routeDefinitionParamsMock.create(); - const mockContext = { - core: coreMock.createRequestHandlerContext(), - licensing: { license: { check: jest.fn().mockReturnValue({ state: 'valid' }) } } as any, - }; - mockContext.core.elasticsearch.client.asCurrentUser.security.deleteRoleMapping.mockResponse({ + const mockCoreContext = coreMock.createRequestHandlerContext(); + const mockLicensingContext = { + license: { check: jest.fn().mockReturnValue({ state: 'valid' }) }, + } as any; + const mockContext = coreMock.createCustomRequestHandlerContext({ + core: mockCoreContext, + licensing: mockLicensingContext, + }); + + mockCoreContext.elasticsearch.client.asCurrentUser.security.deleteRoleMapping.mockResponse({ acknowledged: true, } as any); @@ -39,24 +44,27 @@ describe('DELETE role mappings', () => { expect(response.status).toBe(200); expect(response.payload).toEqual({ acknowledged: true }); expect( - mockContext.core.elasticsearch.client.asCurrentUser.security.deleteRoleMapping + mockCoreContext.elasticsearch.client.asCurrentUser.security.deleteRoleMapping ).toHaveBeenCalledWith({ name }); }); describe('failure', () => { it('returns result of license check', async () => { const mockRouteDefinitionParams = routeDefinitionParamsMock.create(); - const mockContext = { - core: coreMock.createRequestHandlerContext(), - licensing: { - license: { - check: jest.fn().mockReturnValue({ - state: 'invalid', - message: 'test forbidden message', - }), - }, - } as any, - }; + + const mockCoreContext = coreMock.createRequestHandlerContext(); + const mockLicensingContext = { + license: { + check: jest.fn().mockReturnValue({ + state: 'invalid', + message: 'test forbidden message', + }), + }, + } as any; + const mockContext = coreMock.createCustomRequestHandlerContext({ + core: mockCoreContext, + licensing: mockLicensingContext, + }); defineRoleMappingDeleteRoutes(mockRouteDefinitionParams); @@ -75,7 +83,7 @@ describe('DELETE role mappings', () => { expect(response.status).toBe(403); expect(response.payload).toEqual({ message: 'test forbidden message' }); expect( - mockContext.core.elasticsearch.client.asCurrentUser.security.deleteRoleMapping + mockCoreContext.elasticsearch.client.asCurrentUser.security.deleteRoleMapping ).not.toHaveBeenCalled(); }); }); diff --git a/x-pack/plugins/security/server/routes/role_mapping/delete.ts b/x-pack/plugins/security/server/routes/role_mapping/delete.ts index c413ca012fb6..e305de6e4fcb 100644 --- a/x-pack/plugins/security/server/routes/role_mapping/delete.ts +++ b/x-pack/plugins/security/server/routes/role_mapping/delete.ts @@ -23,10 +23,10 @@ export function defineRoleMappingDeleteRoutes({ router }: RouteDefinitionParams) }, createLicensedRouteHandler(async (context, request, response) => { try { - const deleteResponse = - await context.core.elasticsearch.client.asCurrentUser.security.deleteRoleMapping({ - name: request.params.name, - }); + const esClient = (await context.core).elasticsearch.client; + const deleteResponse = await esClient.asCurrentUser.security.deleteRoleMapping({ + name: request.params.name, + }); return response.ok({ body: deleteResponse }); } catch (error) { const wrappedError = wrapError(error); diff --git a/x-pack/plugins/security/server/routes/role_mapping/feature_check.test.ts b/x-pack/plugins/security/server/routes/role_mapping/feature_check.test.ts index 9aa0639b74ce..0efe93d21c1b 100644 --- a/x-pack/plugins/security/server/routes/role_mapping/feature_check.test.ts +++ b/x-pack/plugins/security/server/routes/role_mapping/feature_check.test.ts @@ -48,18 +48,23 @@ describe('GET role mappings feature check', () => { ) => { test(description, async () => { const mockRouteDefinitionParams = routeDefinitionParamsMock.create(); - const mockContext = { - core: coreMock.createRequestHandlerContext(), - licensing: { license: { check: jest.fn().mockReturnValue(licenseCheckResult) } } as any, - }; + const mockCoreContext = coreMock.createRequestHandlerContext(); + const mockLicensingContext = { + license: { + check: jest.fn().mockReturnValue(licenseCheckResult), + }, + } as any; + const mockContext = coreMock.createCustomRequestHandlerContext({ + core: mockCoreContext, + licensing: mockLicensingContext, + }); - mockContext.core.elasticsearch.client.asInternalUser.nodes.info.mockImplementation( - (async () => nodeSettingsResponse()) as any - ); - mockContext.core.elasticsearch.client.asInternalUser.transport.request.mockImplementation( + mockCoreContext.elasticsearch.client.asInternalUser.nodes.info.mockImplementation((async () => + nodeSettingsResponse()) as any); + mockCoreContext.elasticsearch.client.asInternalUser.transport.request.mockImplementation( (async () => xpackUsageResponse()) as any ); - mockContext.core.elasticsearch.client.asCurrentUser.security.hasPrivileges.mockResolvedValue({ + mockCoreContext.elasticsearch.client.asCurrentUser.security.hasPrivileges.mockResolvedValue({ has_all_requested: canManageRoleMappings, } as any); @@ -77,7 +82,7 @@ describe('GET role mappings feature check', () => { expect(response.status).toBe(asserts.statusCode); expect(response.payload).toEqual(asserts.result); - expect(mockContext.licensing.license.check).toHaveBeenCalledWith('security', 'basic'); + expect(mockLicensingContext.license.check).toHaveBeenCalledWith('security', 'basic'); }); }; diff --git a/x-pack/plugins/security/server/routes/role_mapping/feature_check.ts b/x-pack/plugins/security/server/routes/role_mapping/feature_check.ts index 46e0c608b7b0..9715f92cb5a3 100644 --- a/x-pack/plugins/security/server/routes/role_mapping/feature_check.ts +++ b/x-pack/plugins/security/server/routes/role_mapping/feature_check.ts @@ -43,8 +43,9 @@ export function defineRoleMappingFeatureCheckRoute({ router, logger }: RouteDefi validate: false, }, createLicensedRouteHandler(async (context, request, response) => { + const esClient = (await context.core).elasticsearch.client; const { has_all_requested: canManageRoleMappings } = - await context.core.elasticsearch.client.asCurrentUser.security.hasPrivileges({ + await esClient.asCurrentUser.security.hasPrivileges({ body: { cluster: ['manage_security'] }, }); @@ -56,10 +57,7 @@ export function defineRoleMappingFeatureCheckRoute({ router, logger }: RouteDefi }); } - const enabledFeatures = await getEnabledRoleMappingsFeatures( - context.core.elasticsearch.client.asInternalUser, - logger - ); + const enabledFeatures = await getEnabledRoleMappingsFeatures(esClient.asInternalUser, logger); return response.ok({ body: { diff --git a/x-pack/plugins/security/server/routes/role_mapping/get.test.ts b/x-pack/plugins/security/server/routes/role_mapping/get.test.ts index 2b7dcf857bf5..564f93d30235 100644 --- a/x-pack/plugins/security/server/routes/role_mapping/get.test.ts +++ b/x-pack/plugins/security/server/routes/role_mapping/get.test.ts @@ -80,7 +80,11 @@ describe('GET role mappings', () => { headers, }); - const response = await handler(mockContext, mockRequest, kibanaResponseFactory); + const response = await handler( + coreMock.createCustomRequestHandlerContext(mockContext), + mockRequest, + kibanaResponseFactory + ); expect(response.status).toBe(200); expect(response.payload).toEqual([ { @@ -159,7 +163,11 @@ describe('GET role mappings', () => { headers, }); - const response = await handler(mockContext, mockRequest, kibanaResponseFactory); + const response = await handler( + coreMock.createCustomRequestHandlerContext(mockContext), + mockRequest, + kibanaResponseFactory + ); expect(response.status).toBe(200); expect(response.payload).toEqual({ name: 'mapping1', @@ -194,7 +202,11 @@ describe('GET role mappings', () => { headers, }); - const response = await handler(mockContext, mockRequest, kibanaResponseFactory); + const response = await handler( + coreMock.createCustomRequestHandlerContext(mockContext), + mockRequest, + kibanaResponseFactory + ); expect(response.status).toBe(403); expect(response.payload).toEqual({ message: 'test forbidden message' }); expect( @@ -223,7 +235,11 @@ describe('GET role mappings', () => { headers, }); - const response = await handler(mockContext, mockRequest, kibanaResponseFactory); + const response = await handler( + coreMock.createCustomRequestHandlerContext(mockContext), + mockRequest, + kibanaResponseFactory + ); expect(response.status).toBe(404); expect( mockContext.core.elasticsearch.client.asCurrentUser.security.getRoleMapping diff --git a/x-pack/plugins/security/server/routes/role_mapping/get.ts b/x-pack/plugins/security/server/routes/role_mapping/get.ts index 005ba083db57..ac6e7efaa8b0 100644 --- a/x-pack/plugins/security/server/routes/role_mapping/get.ts +++ b/x-pack/plugins/security/server/routes/role_mapping/get.ts @@ -28,10 +28,10 @@ export function defineRoleMappingGetRoutes(params: RouteDefinitionParams) { const expectSingleEntity = typeof request.params.name === 'string'; try { - const roleMappingsResponse = - await context.core.elasticsearch.client.asCurrentUser.security.getRoleMapping({ - name: request.params.name, - }); + const esClient = (await context.core).elasticsearch.client; + const roleMappingsResponse = await esClient.asCurrentUser.security.getRoleMapping({ + name: request.params.name, + }); const mappings = Object.entries(roleMappingsResponse).map(([name, mapping]) => { return { diff --git a/x-pack/plugins/security/server/routes/role_mapping/post.test.ts b/x-pack/plugins/security/server/routes/role_mapping/post.test.ts index e85438ec2603..a6acc866b2d0 100644 --- a/x-pack/plugins/security/server/routes/role_mapping/post.test.ts +++ b/x-pack/plugins/security/server/routes/role_mapping/post.test.ts @@ -45,7 +45,11 @@ describe('POST role mappings', () => { headers, }); - const response = await handler(mockContext, mockRequest, kibanaResponseFactory); + const response = await handler( + coreMock.createCustomRequestHandlerContext(mockContext), + mockRequest, + kibanaResponseFactory + ); expect(response.status).toBe(200); expect(response.payload).toEqual({ created: true }); @@ -91,7 +95,11 @@ describe('POST role mappings', () => { headers, }); - const response = await handler(mockContext, mockRequest, kibanaResponseFactory); + const response = await handler( + coreMock.createCustomRequestHandlerContext(mockContext), + mockRequest, + kibanaResponseFactory + ); expect(response.status).toBe(403); expect(response.payload).toEqual({ message: 'test forbidden message' }); diff --git a/x-pack/plugins/security/server/routes/role_mapping/post.ts b/x-pack/plugins/security/server/routes/role_mapping/post.ts index 1a276e46e242..79bd8b3d3f2c 100644 --- a/x-pack/plugins/security/server/routes/role_mapping/post.ts +++ b/x-pack/plugins/security/server/routes/role_mapping/post.ts @@ -44,11 +44,11 @@ export function defineRoleMappingPostRoutes({ router }: RouteDefinitionParams) { }, createLicensedRouteHandler(async (context, request, response) => { try { - const saveResponse = - await context.core.elasticsearch.client.asCurrentUser.security.putRoleMapping({ - name: request.params.name, - body: request.body, - }); + const esClient = (await context.core).elasticsearch.client; + const saveResponse = await esClient.asCurrentUser.security.putRoleMapping({ + name: request.params.name, + body: request.body, + }); return response.ok({ body: saveResponse }); } catch (error) { const wrappedError = wrapError(error); diff --git a/x-pack/plugins/security/server/routes/security_checkup/get_state.ts b/x-pack/plugins/security/server/routes/security_checkup/get_state.ts index 8c4e69cb87c8..2946c3fa5dee 100644 --- a/x-pack/plugins/security/server/routes/security_checkup/get_state.ts +++ b/x-pack/plugins/security/server/routes/security_checkup/get_state.ts @@ -31,12 +31,10 @@ export function defineSecurityCheckupGetStateRoutes({ router.get( { path: '/internal/security/security_checkup/state', validate: false }, async (context, _request, response) => { + const esClient = (await context.core).elasticsearch.client; let displayAlert = false; if (showInsecureClusterWarning) { - displayAlert = await doesClusterHaveUserData( - context.core.elasticsearch.client.asInternalUser, - logger - ); + displayAlert = await doesClusterHaveUserData(esClient.asInternalUser, logger); } const state: SecurityCheckupState = { diff --git a/x-pack/plugins/security/server/routes/users/change_password.test.ts b/x-pack/plugins/security/server/routes/users/change_password.test.ts index e51824a4332d..19f84ce46162 100644 --- a/x-pack/plugins/security/server/routes/users/change_password.test.ts +++ b/x-pack/plugins/security/server/routes/users/change_password.test.ts @@ -32,13 +32,14 @@ describe('Change password', () => { let routeHandler: RequestHandler; let routeConfig: RouteConfig; let mockContext: DeeplyMockedKeys; + let mockCoreContext: ReturnType; function checkPasswordChangeAPICall(username: string, headers?: Headers) { expect( - mockContext.core.elasticsearch.client.asCurrentUser.security.changePassword + mockCoreContext.elasticsearch.client.asCurrentUser.security.changePassword ).toHaveBeenCalledTimes(1); expect( - mockContext.core.elasticsearch.client.asCurrentUser.security.changePassword + mockCoreContext.elasticsearch.client.asCurrentUser.security.changePassword ).toHaveBeenCalledWith( { username, body: { password: 'new-password' } }, headers && { headers } @@ -59,10 +60,11 @@ describe('Change password', () => { authc.login.mockResolvedValue(AuthenticationResult.succeeded(mockAuthenticatedUser())); session.get.mockResolvedValue(sessionMock.createValue()); - mockContext = { - core: coreMock.createRequestHandlerContext(), + mockCoreContext = coreMock.createRequestHandlerContext(); + mockContext = coreMock.createCustomRequestHandlerContext({ + core: mockCoreContext, licensing: { license: { check: jest.fn().mockReturnValue({ state: 'valid' }) } }, - } as any; + }) as any; defineChangeUserPasswordRoutes(routeParamsMock); @@ -113,7 +115,7 @@ describe('Change password', () => { const changePasswordFailure = new errors.ResponseError( securityMock.createApiResponse({ statusCode: 401, body: {} }) ); - mockContext.core.elasticsearch.client.asCurrentUser.security.changePassword.mockRejectedValue( + mockCoreContext.elasticsearch.client.asCurrentUser.security.changePassword.mockRejectedValue( changePasswordFailure ); @@ -150,7 +152,7 @@ describe('Change password', () => { it('returns 500 if password update request fails with non-401 error.', async () => { const failureReason = new Error('Request failed.'); - mockContext.core.elasticsearch.client.asCurrentUser.security.changePassword.mockRejectedValue( + mockCoreContext.elasticsearch.client.asCurrentUser.security.changePassword.mockRejectedValue( failureReason ); @@ -232,7 +234,7 @@ describe('Change password', () => { it('returns 500 if password update request fails.', async () => { const failureReason = new Error('Request failed.'); - mockContext.core.elasticsearch.client.asCurrentUser.security.changePassword.mockRejectedValue( + mockCoreContext.elasticsearch.client.asCurrentUser.security.changePassword.mockRejectedValue( failureReason ); diff --git a/x-pack/plugins/security/server/routes/users/change_password.ts b/x-pack/plugins/security/server/routes/users/change_password.ts index 4dddd0cb0758..51df3e906a60 100644 --- a/x-pack/plugins/security/server/routes/users/change_password.ts +++ b/x-pack/plugins/security/server/routes/users/change_password.ts @@ -59,7 +59,8 @@ export function defineChangeUserPasswordRoutes({ : undefined; try { - await context.core.elasticsearch.client.asCurrentUser.security.changePassword( + const esClient = (await context.core).elasticsearch.client; + await esClient.asCurrentUser.security.changePassword( { username, body: { password: newPassword } }, options ); diff --git a/x-pack/plugins/security/server/routes/users/create_or_update.ts b/x-pack/plugins/security/server/routes/users/create_or_update.ts index 75838240e79f..de6adad78b4e 100644 --- a/x-pack/plugins/security/server/routes/users/create_or_update.ts +++ b/x-pack/plugins/security/server/routes/users/create_or_update.ts @@ -30,7 +30,8 @@ export function defineCreateOrUpdateUserRoutes({ router }: RouteDefinitionParams }, createLicensedRouteHandler(async (context, request, response) => { try { - await context.core.elasticsearch.client.asCurrentUser.security.putUser({ + const esClient = (await context.core).elasticsearch.client; + await esClient.asCurrentUser.security.putUser({ username: request.params.username, body: request.body, }); diff --git a/x-pack/plugins/security/server/routes/users/delete.ts b/x-pack/plugins/security/server/routes/users/delete.ts index cc86b522f1c4..429adb368574 100644 --- a/x-pack/plugins/security/server/routes/users/delete.ts +++ b/x-pack/plugins/security/server/routes/users/delete.ts @@ -21,7 +21,8 @@ export function defineDeleteUserRoutes({ router }: RouteDefinitionParams) { }, createLicensedRouteHandler(async (context, request, response) => { try { - await context.core.elasticsearch.client.asCurrentUser.security.deleteUser({ + const esClient = (await context.core).elasticsearch.client; + await esClient.asCurrentUser.security.deleteUser({ username: request.params.username, }); diff --git a/x-pack/plugins/security/server/routes/users/disable.ts b/x-pack/plugins/security/server/routes/users/disable.ts index d68da8b8490f..87f61daca8c9 100644 --- a/x-pack/plugins/security/server/routes/users/disable.ts +++ b/x-pack/plugins/security/server/routes/users/disable.ts @@ -21,7 +21,8 @@ export function defineDisableUserRoutes({ router }: RouteDefinitionParams) { }, createLicensedRouteHandler(async (context, request, response) => { try { - await context.core.elasticsearch.client.asCurrentUser.security.disableUser({ + const esClient = (await context.core).elasticsearch.client; + await esClient.asCurrentUser.security.disableUser({ username: request.params.username, }); diff --git a/x-pack/plugins/security/server/routes/users/enable.ts b/x-pack/plugins/security/server/routes/users/enable.ts index 8a522e63f7fd..a8a9d62bee93 100644 --- a/x-pack/plugins/security/server/routes/users/enable.ts +++ b/x-pack/plugins/security/server/routes/users/enable.ts @@ -21,7 +21,8 @@ export function defineEnableUserRoutes({ router }: RouteDefinitionParams) { }, createLicensedRouteHandler(async (context, request, response) => { try { - await context.core.elasticsearch.client.asCurrentUser.security.enableUser({ + const esClient = (await context.core).elasticsearch.client; + await esClient.asCurrentUser.security.enableUser({ username: request.params.username, }); diff --git a/x-pack/plugins/security/server/routes/users/get.ts b/x-pack/plugins/security/server/routes/users/get.ts index 3f2804e3c161..ed18c8437627 100644 --- a/x-pack/plugins/security/server/routes/users/get.ts +++ b/x-pack/plugins/security/server/routes/users/get.ts @@ -21,8 +21,9 @@ export function defineGetUserRoutes({ router }: RouteDefinitionParams) { }, createLicensedRouteHandler(async (context, request, response) => { try { + const esClient = (await context.core).elasticsearch.client; const username = request.params.username; - const users = await context.core.elasticsearch.client.asCurrentUser.security.getUser({ + const users = await esClient.asCurrentUser.security.getUser({ username, }); diff --git a/x-pack/plugins/security/server/routes/users/get_all.ts b/x-pack/plugins/security/server/routes/users/get_all.ts index ce9d84aca2a0..eae066418934 100644 --- a/x-pack/plugins/security/server/routes/users/get_all.ts +++ b/x-pack/plugins/security/server/routes/users/get_all.ts @@ -14,11 +14,10 @@ export function defineGetAllUsersRoutes({ router }: RouteDefinitionParams) { { path: '/internal/security/users', validate: false }, createLicensedRouteHandler(async (context, request, response) => { try { + const esClient = (await context.core).elasticsearch.client; return response.ok({ // Return only values since keys (user names) are already duplicated there. - body: Object.values( - await context.core.elasticsearch.client.asCurrentUser.security.getUser() - ), + body: Object.values(await esClient.asCurrentUser.security.getUser()), }); } catch (error) { return response.customError(wrapIntoCustomErrorResponse(error)); diff --git a/x-pack/plugins/security/server/types.ts b/x-pack/plugins/security/server/types.ts index 1f8e2d0ca0b9..6227696a9fbc 100644 --- a/x-pack/plugins/security/server/types.ts +++ b/x-pack/plugins/security/server/types.ts @@ -5,15 +5,15 @@ * 2.0. */ -import type { IRouter, RequestHandlerContext } from '@kbn/core/server'; +import type { CustomRequestHandlerContext, IRouter } from '@kbn/core/server'; import type { LicensingApiRequestHandlerContext } from '@kbn/licensing-plugin/server'; /** * @internal */ -export interface SecurityRequestHandlerContext extends RequestHandlerContext { +export type SecurityRequestHandlerContext = CustomRequestHandlerContext<{ licensing: LicensingApiRequestHandlerContext; -} +}>; /** * @internal diff --git a/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.ts b/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.ts index a5476159a03a..317ee194ccc2 100644 --- a/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.ts +++ b/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.ts @@ -8,7 +8,7 @@ import { schema } from '@kbn/config-schema'; import { ConditionEntryField, OperatingSystem } from '@kbn/securitysolution-utils'; import { TrustedAppConditionEntry } from '../types'; -import { getDuplicateFields, isValidHash } from '../service/trusted_apps/validations'; +import { getDuplicateFields, isValidHash } from '../service/artifacts/validations'; export const DeleteTrustedAppsRequestSchema = { params: schema.object({ diff --git a/x-pack/plugins/security_solution/common/endpoint/service/trusted_apps/validations.ts b/x-pack/plugins/security_solution/common/endpoint/service/artifacts/validations.ts similarity index 100% rename from x-pack/plugins/security_solution/common/endpoint/service/trusted_apps/validations.ts rename to x-pack/plugins/security_solution/common/endpoint/service/artifacts/validations.ts diff --git a/x-pack/plugins/security_solution/common/experimental_features.ts b/x-pack/plugins/security_solution/common/experimental_features.ts index 63a626055c7c..2a582bbc0417 100644 --- a/x-pack/plugins/security_solution/common/experimental_features.ts +++ b/x-pack/plugins/security_solution/common/experimental_features.ts @@ -12,7 +12,6 @@ export type ExperimentalFeatures = typeof allowedExperimentalValues; * This object is then used to validate and parse the value entered. */ export const allowedExperimentalValues = Object.freeze({ - ruleRegistryEnabled: true, tGridEnabled: true, tGridEventRenderedViewEnabled: true, excludePoliciesInFilterEnabled: false, diff --git a/x-pack/plugins/security_solution/kibana.json b/x-pack/plugins/security_solution/kibana.json index faece05732b7..af0bd1230311 100644 --- a/x-pack/plugins/security_solution/kibana.json +++ b/x-pack/plugins/security_solution/kibana.json @@ -54,6 +54,7 @@ "kibanaReact", "usageCollection", "lists", - "ml" + "ml", + "unifiedSearch" ] } diff --git a/x-pack/plugins/security_solution/public/common/components/authentication/__snapshots__/authentications_host_table.test.tsx.snap b/x-pack/plugins/security_solution/public/common/components/authentication/__snapshots__/authentications_host_table.test.tsx.snap index b0a55bb72acb..ff496768397c 100644 --- a/x-pack/plugins/security_solution/public/common/components/authentication/__snapshots__/authentications_host_table.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/common/components/authentication/__snapshots__/authentications_host_table.test.tsx.snap @@ -19,6 +19,14 @@ exports[`Authentication Host Table Component rendering it renders the host authe user-select: text; } +.c1.toggle-expand .header-section-content { + height: 48px; +} + +.c1.toggle-expand .header-section-titles { + margin-top: 16px; +} + .c0 { position: relative; } @@ -52,7 +60,7 @@ exports[`Authentication Host Table Component rendering it renders the host authe data-test-subj="authentications-host-table-loading-false" >

{ + const actual = jest.requireActual('@elastic/charts'); + return { + ...actual, + Chart: jest.fn(({ children, ...props }) => ( +
+ {children} +
+ )), + Partition: jest.fn((props) =>
), + Settings: jest.fn((props) =>
), + }; +}); + +jest.mock('uuid', () => { + const actual = jest.requireActual('uuid'); + + return { + ...actual, + v4: jest.fn().mockReturnValue('test-uuid'), + }; +}); + +jest.mock('../../../overview/components/detection_response/alerts_by_status/chart_label', () => { + return { + ChartLabel: jest.fn((props) => ), + }; +}); + +jest.mock('./draggable_legend', () => { + return { + DraggableLegend: jest.fn((props) => ), + }; +}); + +jest.mock('./common', () => { + return { + useTheme: jest.fn(() => ({ + eui: { + euiScrollBar: 0, + euiColorDarkShade: '#fff', + euiScrollBarCorner: '#ccc', + }, + })), + }; +}); + +const testColors = { + critical: '#EF6550', + high: '#EE9266', + medium: '#F3B689', + low: '#F8D9B2', +}; + +describe('DonutChart', () => { + const props: DonutChartProps = { + data: parsedMockAlertsData?.open?.severities, + label: 'Open', + link: null, + title: , + fillColor: jest.fn(() => '#ccc'), + totalCount: parsedMockAlertsData?.open?.total, + legendItems: (['critical', 'high', 'medium', 'low'] as Severity[]).map((d) => ({ + color: testColors[d], + dataProviderId: escapeDataProviderId(`draggable-legend-item-${uuid.v4()}-${d}`), + timelineId: undefined, + field: 'kibana.alert.severity', + value: d, + })), + }; + + beforeEach(() => { + jest.clearAllMocks(); + }); + test('should render Chart', () => { + const { container } = render(); + expect(container.querySelector(`[data-test-subj="es-chart"]`)).toBeInTheDocument(); + }); + + test('should render chart Settings', () => { + const { container } = render(); + expect(container.querySelector(`[data-test-subj="es-chart-settings"]`)).toBeInTheDocument(); + + expect((Settings as jest.Mock).mock.calls[0][0]).toEqual({ + baseTheme: { + eui: { + euiColorDarkShade: '#fff', + euiScrollBar: 0, + euiScrollBarCorner: '#ccc', + }, + }, + theme: { + chartMargins: { bottom: 0, left: 0, right: 0, top: 0 }, + partition: { + circlePadding: 4, + emptySizeRatio: 0.8, + idealFontSizeJump: 1.1, + outerSizeRatio: 1, + }, + }, + }); + }); + + test('should render an empty chart', () => { + const testProps = { + ...props, + data: parsedMockAlertsData?.acknowledged?.severities, + label: 'Acknowledged', + title: , + totalCount: parsedMockAlertsData?.acknowledged?.total, + }; + const { container } = render(); + expect(container.querySelector(`[data-test-subj="empty-donut"]`)).toBeInTheDocument(); + }); + + test('should render chart Partition', () => { + const { container } = render(); + expect(container.querySelector(`[data-test-subj="es-chart-partition"]`)).toBeInTheDocument(); + expect((Partition as jest.Mock).mock.calls[0][0].data).toEqual( + parsedMockAlertsData?.open?.severities + ); + expect((Partition as jest.Mock).mock.calls[0][0].layout).toEqual('sunburst'); + }); + + test('should render chart legend', () => { + const { container } = render(); + expect(container.querySelector(`[data-test-subj="draggable-legend"]`)).toBeInTheDocument(); + expect((DraggableLegend as unknown as jest.Mock).mock.calls[0][0].legendItems).toEqual([ + { + color: '#EF6550', + dataProviderId: 'draggable-legend-item-test-uuid-critical', + field: 'kibana.alert.severity', + timelineId: undefined, + value: 'critical', + }, + { + color: '#EE9266', + dataProviderId: 'draggable-legend-item-test-uuid-high', + field: 'kibana.alert.severity', + timelineId: undefined, + value: 'high', + }, + { + color: '#F3B689', + dataProviderId: 'draggable-legend-item-test-uuid-medium', + field: 'kibana.alert.severity', + timelineId: undefined, + value: 'medium', + }, + { + color: '#F8D9B2', + dataProviderId: 'draggable-legend-item-test-uuid-low', + field: 'kibana.alert.severity', + timelineId: undefined, + value: 'low', + }, + ]); + }); + + test('should NOT render chart legend if showLegend is false', () => { + const testProps = { + ...props, + legendItems: null, + }; + const { container } = render(); + expect(container.querySelector(`[data-test-subj="legend"]`)).not.toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/charts/donutchart.tsx b/x-pack/plugins/security_solution/public/common/components/charts/donutchart.tsx new file mode 100644 index 000000000000..2708fefc7652 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/charts/donutchart.tsx @@ -0,0 +1,149 @@ +/* + * 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 { EuiFlexGroup, EuiFlexItem, EuiText, useEuiTheme } from '@elastic/eui'; +import React, { useMemo } from 'react'; + +import { + Chart, + Datum, + Partition, + Settings, + PartitionLayout, + defaultPartitionValueFormatter, + NodeColorAccessor, + PartialTheme, +} from '@elastic/charts'; +import styled from 'styled-components'; +import { useTheme } from './common'; +import { DraggableLegend } from './draggable_legend'; +import { LegendItem } from './draggable_legend_item'; +import { DonutChartEmpty } from './donutchart_empty'; + +export const NO_LEGEND_DATA: LegendItem[] = []; + +const donutTheme: PartialTheme = { + chartMargins: { top: 0, bottom: 0, left: 0, right: 0 }, + partition: { + idealFontSizeJump: 1.1, + outerSizeRatio: 1, + emptySizeRatio: 0.8, + circlePadding: 4, + }, +}; + +interface DonutChartData { + key: string; + value: number; + group?: string; + label?: string; +} + +export type FillColor = string | NodeColorAccessor; +export interface DonutChartProps { + data: DonutChartData[] | null | undefined; + fillColor: FillColor; + height?: number; + label: string; + legendItems?: LegendItem[] | null | undefined; + link?: string | null; + title: React.ReactElement | string | number | null; + totalCount: number | null | undefined; +} + +/* Make this position absolute in order to overlap the text onto the donut */ +const DonutTextWrapper = styled(EuiFlexGroup)` + top: 34%; + width: 100%; + max-width: 77px; + position: absolute; + z-index: 1; +`; + +const StyledEuiFlexItem = styled(EuiFlexItem)` + position: relative; + align-items: center; +`; + +export const DonutChart = ({ + data, + fillColor, + height = 90, + label, + legendItems, + link, + title, + totalCount, +}: DonutChartProps) => { + const theme = useTheme(); + const { euiTheme } = useEuiTheme(); + const emptyLabelStyle = useMemo( + () => ({ + color: euiTheme.colors.disabled, + }), + [euiTheme.colors.disabled] + ); + return ( + + + + {title} + + {data ? ( + + {label} + + ) : ( + + {label} + + )} + + + {data == null || totalCount == null || totalCount === 0 ? ( + + ) : ( + + + d.value as number} + valueFormatter={(d: number) => `${defaultPartitionValueFormatter(d)}`} + layers={[ + { + groupByRollup: (d: Datum) => d.label ?? d.key, + nodeLabel: (d: Datum) => d, + shape: { + fillColor, + }, + }, + ]} + /> + + )} + + {legendItems && legendItems?.length > 0 && ( + + + + )} + + ); +}; diff --git a/x-pack/plugins/security_solution/public/common/components/charts/donutchart_empty.test.tsx b/x-pack/plugins/security_solution/public/common/components/charts/donutchart_empty.test.tsx new file mode 100644 index 000000000000..858347226272 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/charts/donutchart_empty.test.tsx @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { render } from '@testing-library/react'; +import React from 'react'; +import { DonutChartEmpty } from './donutchart_empty'; + +describe('DonutChartEmpty', () => { + test('render', () => { + const { container } = render(); + expect(container.querySelector(`[data-test-subj="empty-donut"]`)).toBeInTheDocument(); + expect(container.querySelector(`[data-test-subj="empty-donut-small"]`)).toBeInTheDocument(); + }); + + test('does Not render', () => { + const props = { + size: 90, + donutWidth: 90, + }; + const { container } = render(); + expect(container.querySelector(`[data-test-subj="empty-donut"]`)).not.toBeInTheDocument(); + expect(container.querySelector(`[data-test-subj="empty-donut-small"]`)).not.toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/charts/donutchart_empty.tsx b/x-pack/plugins/security_solution/public/common/components/charts/donutchart_empty.tsx new file mode 100644 index 000000000000..a378f94bfbe1 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/charts/donutchart_empty.tsx @@ -0,0 +1,44 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; +import styled from 'styled-components'; + +interface DonutChartEmptyProps { + size?: number; + donutWidth?: number; +} + +export const emptyDonutColor = '#FAFBFD'; + +const BigRing = styled.div` + border-radius: 50%; + ${({ size }) => + `height: ${size}px; + width: ${size}px; + background-color: ${emptyDonutColor}; + text-align: center; + line-height: ${size}px;`} +`; + +const SmallRing = styled.div` + border-radius: 50%; + ${({ size }) => ` + height: ${size}px; + width: ${size}px; + background-color: white; + display: inline-block; + vertical-align: middle;`} +`; + +const EmptyDonutChartComponent: React.FC = ({ size = 90, donutWidth = 20 }) => + size - donutWidth > 0 ? ( + + + + ) : null; + +export const DonutChartEmpty = React.memo(EmptyDonutChartComponent); diff --git a/x-pack/plugins/security_solution/public/common/components/charts/legend.tsx b/x-pack/plugins/security_solution/public/common/components/charts/legend.tsx new file mode 100644 index 000000000000..d01d9057c697 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/charts/legend.tsx @@ -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; you may not use this file except in compliance with the Elastic License + * 2.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 { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiText } from '@elastic/eui'; +import React from 'react'; + +import { LegendItem } from './legend_item'; + +const LegendComponent: React.FC<{ + legendItems: LegendItem[]; +}> = ({ legendItems }) => { + if (legendItems.length === 0) { + return null; + } + + return ( + + + {legendItems.map((item, i) => ( + + + + + ))} + + + ); +}; + +LegendComponent.displayName = 'LegendComponent'; + +export const Legend = React.memo(LegendComponent); diff --git a/x-pack/plugins/security_solution/public/common/components/charts/legend_item.tsx b/x-pack/plugins/security_solution/public/common/components/charts/legend_item.tsx new file mode 100644 index 000000000000..867a48294c09 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/charts/legend_item.tsx @@ -0,0 +1,61 @@ +/* + * 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 { EuiFlexGroup, EuiFlexItem, EuiHealth, EuiText } from '@elastic/eui'; +import React from 'react'; +import styled from 'styled-components'; + +import { EMPTY_VALUE_LABEL } from './translation'; +import { hasValueToDisplay } from '../../utils/validators'; + +export interface LegendItem { + color?: string; + field: string; + value: string | number; +} + +const LegendText = styled.span` + font-size: 10.5px; +`; + +/** + * Renders the value or a placeholder in case the value is empty + */ +const ValueWrapper = React.memo<{ value: LegendItem['value'] }>(({ value }) => + hasValueToDisplay(value) ? ( + {value} + ) : ( + {EMPTY_VALUE_LABEL} + ) +); + +ValueWrapper.displayName = 'ValueWrapper'; + +const LegendItemComponent: React.FC<{ + legendItem: LegendItem; +}> = ({ legendItem }) => { + const { color, value } = legendItem; + + return ( + + + {color != null && ( + + + + )} + + + + + + ); +}; + +LegendItemComponent.displayName = 'LegendItemComponent'; + +export const LegendItem = React.memo(LegendItemComponent); diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_flyout/index.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_flyout/index.tsx index ebe77ccfce55..0e063a05bab0 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_flyout/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_flyout/index.tsx @@ -135,7 +135,7 @@ export const AddExceptionFlyout = memo(function AddExceptionFlyout({ onRuleChange, alertStatus, }: AddExceptionFlyoutProps) { - const { http, data } = useKibana().services; + const { http, unifiedSearch } = useKibana().services; const [errorsExist, setErrorExists] = useState(false); const [comment, setComment] = useState(''); const { rule: maybeRule, loading: isRuleLoading } = useRuleAsync(ruleId); @@ -505,7 +505,7 @@ export const AddExceptionFlyout = memo(function AddExceptionFlyout({ allowLargeValueLists: !isEqlRule(maybeRule?.type) && !isThresholdRule(maybeRule?.type), httpService: http, - autocompleteService: data.autocomplete, + autocompleteService: unifiedSearch.autocomplete, exceptionListItems: initialExceptionItems, listType: exceptionListType, osTypes: osTypesSelection, diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_flyout/index.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_flyout/index.tsx index cfa8b08b901d..0097f2d02cb3 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_flyout/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_flyout/index.tsx @@ -112,7 +112,7 @@ export const EditExceptionFlyout = memo(function EditExceptionFlyout({ onConfirm, onRuleChange, }: EditExceptionFlyoutProps) { - const { http, data } = useKibana().services; + const { http, unifiedSearch } = useKibana().services; const [comment, setComment] = useState(''); const [errorsExist, setErrorExists] = useState(false); const { rule: maybeRule, loading: isRuleLoading } = useRuleAsync(ruleId); @@ -360,7 +360,7 @@ export const EditExceptionFlyout = memo(function EditExceptionFlyout({ allowLargeValueLists: !isEqlRule(maybeRule?.type) && !isThresholdRule(maybeRule?.type), httpService: http, - autocompleteService: data.autocomplete, + autocompleteService: unifiedSearch.autocomplete, exceptionListItems: [exceptionItem], listType: exceptionListType, listId: exceptionItem.list_id, diff --git a/x-pack/plugins/security_solution/public/resolver/view/submenu.test.tsx b/x-pack/plugins/security_solution/public/common/components/formatted_number/index.test.tsx similarity index 95% rename from x-pack/plugins/security_solution/public/resolver/view/submenu.test.tsx rename to x-pack/plugins/security_solution/public/common/components/formatted_number/index.test.tsx index 8cfaf3fe7167..bbf53c5a38aa 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/submenu.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/formatted_number/index.test.tsx @@ -5,9 +5,9 @@ * 2.0. */ -import { compactNotationParts } from './submenu'; +import { compactNotationParts } from '.'; -describe('The Resolver node pills number presentation', () => { +describe('compactNotationParts', () => { describe('When given a small number under 1000', () => { it('does not change the presentation of small numbers', () => { expect(compactNotationParts(1)).toEqual([1, '', '']); diff --git a/x-pack/plugins/security_solution/public/common/components/formatted_number/index.tsx b/x-pack/plugins/security_solution/public/common/components/formatted_number/index.tsx new file mode 100644 index 000000000000..ebab730d5a96 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/formatted_number/index.tsx @@ -0,0 +1,81 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiI18nNumber } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import React, { useMemo } from 'react'; + +/** + * Until browser support accomodates the `notation="compact"` feature of Intl.NumberFormat... + * exported for testing + * @param num The number to format + * @returns [mantissa ("12" in "12k+"), Scalar of compact notation (k,M,B,T), remainder indicator ("+" in "12k+")] + */ +export function compactNotationParts( + num: number +): [mantissa: number, compactNotation: string, remainderIndicator: string] { + if (!Number.isFinite(num)) { + return [num, '', '']; + } + + // "scale" here will be a term indicating how many thousands there are in the number + // e.g. 1001 will be 1000, 1000002 will be 1000000, etc. + const scale = Math.pow(10, 3 * Math.min(Math.floor(Math.floor(Math.log10(num)) / 3), 4)); + + const compactPrefixTranslations = { + compactThousands: i18n.translate('xpack.securitySolution.formattedNumber.compactThousands', { + defaultMessage: 'k', + }), + compactMillions: i18n.translate('xpack.securitySolution.formattedNumber.compactMillions', { + defaultMessage: 'M', + }), + + compactBillions: i18n.translate('xpack.securitySolution.formattedNumber.compactBillions', { + defaultMessage: 'B', + }), + + compactTrillions: i18n.translate('xpack.securitySolution.formattedNumber.compactTrillions', { + defaultMessage: 'T', + }), + }; + const prefixMap: Map = new Map([ + [1, ''], + [1000, compactPrefixTranslations.compactThousands], + [1000000, compactPrefixTranslations.compactMillions], + [1000000000, compactPrefixTranslations.compactBillions], + [1000000000000, compactPrefixTranslations.compactTrillions], + ]); + const hasRemainder = i18n.translate('xpack.securitySolution.formattedNumber.compactOverflow', { + defaultMessage: '+', + }); + const prefix = prefixMap.get(scale) ?? ''; + return [Math.floor(num / scale), prefix, (num / scale) % 1 > Number.EPSILON ? hasRemainder : '']; +} + +const FormattedCountComponent: React.FC<{ count: number | null }> = ({ count }) => { + const [mantissa, scale, hasRemainder] = useMemo(() => compactNotationParts(count || 0), [count]); + + if (count == null) { + return null; + } + + if (count === 0) { + return <>{0}; + } + + return ( + , scale, hasRemainder }} + /> + ); +}; + +export const FormattedCount = React.memo(FormattedCountComponent); diff --git a/x-pack/plugins/security_solution/public/common/components/header_section/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/common/components/header_section/__snapshots__/index.test.tsx.snap index 45a6e20cf087..4447f631c157 100644 --- a/x-pack/plugins/security_solution/public/common/components/header_section/__snapshots__/index.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/common/components/header_section/__snapshots__/index.test.tsx.snap @@ -2,6 +2,8 @@ exports[`HeaderSection it renders 1`] = `
diff --git a/x-pack/plugins/security_solution/public/common/components/header_section/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/header_section/index.test.tsx index a03999479e39..d835f4b2b236 100644 --- a/x-pack/plugins/security_solution/public/common/components/header_section/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/header_section/index.test.tsx @@ -231,6 +231,28 @@ describe('HeaderSection', () => { expect(wrapper.find('[data-test-subj="header-section-filters"]').first().exists()).toBe(true); expect(wrapper.find('[data-test-subj="inspect-icon-button"]').first().exists()).toBe(true); }); + + test('it appends `toggle-expand` class to Header when toggleStatus = true', () => { + const wrapper = mount( + + +

{'Test children'}

+
+
+ ); + + expect(wrapper.find('[data-test-subj="header-section"]').first().prop('className')).toBe( + 'toggle-expand siemHeaderSection' + ); + }); + test('it does not render anything but title when toggleStatus = false', () => { const wrapper = mount( diff --git a/x-pack/plugins/security_solution/public/common/components/header_section/index.tsx b/x-pack/plugins/security_solution/public/common/components/header_section/index.tsx index 7997dfa83e27..86cdc45312cf 100644 --- a/x-pack/plugins/security_solution/public/common/components/header_section/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/header_section/index.tsx @@ -16,6 +16,7 @@ import { import React, { useCallback } from 'react'; import styled, { css } from 'styled-components'; +import classnames from 'classnames'; import { InspectButton } from '../inspect'; import { Subtitle } from '../subtitle'; @@ -24,11 +25,23 @@ import * as i18n from '../../containers/query_toggle/translations'; interface HeaderProps { border?: boolean; height?: number; + className?: string; + $hideSubtitle?: boolean; } -const Header = styled.header.attrs(() => ({ - className: 'siemHeaderSection', -}))` +const Header = styled.header` + &.toggle-expand { + .header-section-content { + height: 48px; + } + + ${({ $hideSubtitle, theme }) => + !$hideSubtitle && + `.header-section-titles { + margin-top: ${theme.eui.paddingSizes.m}; + }`} + } + ${({ height }) => height && css` @@ -91,17 +104,33 @@ const HeaderSectionComponent: React.FC = ({ toggleQuery(!toggleStatus); } }, [toggleQuery, toggleStatus]); + + const classNames = classnames({ + 'toggle-expand': toggleStatus, + siemHeaderSection: true, + }); return ( -
+
- + - + {toggleQuery && ( { }); describe('#onSavedQueryUpdated', () => { + beforeEach(() => { + const autocompleteStart = unifiedSearchPluginMock.createStartContract(); + setAutocomplete(autocompleteStart.autocomplete); + }); + test('is only reference that changed when dataProviders props get updated', async () => { const wrapper = await getWrapper( { beforeEach(() => { (useKibana as jest.Mock).mockReturnValue({ services: { - data: { + unifiedSearch: { autocomplete: { getValueSuggestions: getValueSuggestionsMock, }, diff --git a/x-pack/plugins/security_solution/public/common/components/threat_match/list_item.test.tsx b/x-pack/plugins/security_solution/public/common/components/threat_match/list_item.test.tsx index b49bb1a7fb89..571207918c44 100644 --- a/x-pack/plugins/security_solution/public/common/components/threat_match/list_item.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/threat_match/list_item.test.tsx @@ -56,7 +56,7 @@ describe('ListItemComponent', () => { beforeAll(() => { (useKibana as jest.Mock).mockReturnValue({ services: { - data: { + unifiedSearch: { autocomplete: { getValueSuggestions: getValueSuggestionsMock, }, diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_add_to_existing_case.test.tsx b/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_add_to_existing_case.test.tsx index 5b16527fff54..2b75b149855f 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_add_to_existing_case.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_add_to_existing_case.test.tsx @@ -12,7 +12,7 @@ import { useAddToExistingCase } from './use_add_to_existing_case'; jest.mock('../../lib/kibana/kibana_react'); -describe('', () => { +describe('useAddToExistingCase', () => { const mockCases = mockCasesContract(); const mockOnAddToCaseClicked = jest.fn(); const timeRange = { diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_add_to_new_case.test.tsx b/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_add_to_new_case.test.tsx index 35a05281b932..7b5a8b09e5f5 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_add_to_new_case.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/use_add_to_new_case.test.tsx @@ -12,7 +12,7 @@ import { useAddToNewCase } from './use_add_to_new_case'; jest.mock('../../lib/kibana/kibana_react'); -describe('', () => { +describe('useAddToNewCase', () => { const mockCases = mockCasesContract(); const timeRange = { from: '2022-03-06T16:00:00.000Z', diff --git a/x-pack/plugins/security_solution/public/common/lib/kibana/kibana_react.mock.ts b/x-pack/plugins/security_solution/public/common/lib/kibana/kibana_react.mock.ts index 4cab4e2722ee..0097e31cfee8 100644 --- a/x-pack/plugins/security_solution/public/common/lib/kibana/kibana_react.mock.ts +++ b/x-pack/plugins/security_solution/public/common/lib/kibana/kibana_react.mock.ts @@ -10,6 +10,7 @@ import React from 'react'; import { RecursivePartial } from '@elastic/eui/src/components/common'; +import { unifiedSearchPluginMock } from '@kbn/unified-search-plugin/public/mocks'; import { coreMock, themeServiceMock } from '@kbn/core/public/mocks'; import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; @@ -95,6 +96,7 @@ export const createStartServicesMock = ( const urlService = new MockUrlService(); const locator = urlService.locators.create(new MlLocatorDefinition()); const fleet = fleetMock.createStartMock(); + const unifiedSearch = unifiedSearchPluginMock.createStartContract(); return { ...core, @@ -105,6 +107,7 @@ export const createStartServicesMock = ( getCreateCase: jest.fn(), getRecentCases: jest.fn(), }, + unifiedSearch, data: { ...data, query: { diff --git a/x-pack/plugins/security_solution/public/common/mock/endpoint/app_context_render.tsx b/x-pack/plugins/security_solution/public/common/mock/endpoint/app_context_render.tsx index 6fdbffe83efc..2e533230e0a0 100644 --- a/x-pack/plugins/security_solution/public/common/mock/endpoint/app_context_render.tsx +++ b/x-pack/plugins/security_solution/public/common/mock/endpoint/app_context_render.tsx @@ -46,7 +46,7 @@ export interface AppContextTestRender { store: Store; history: ReturnType; coreStart: ReturnType; - depsStart: Pick; + depsStart: Pick; startServices: StartServices; middlewareSpy: MiddlewareActionSpyHelper; /** diff --git a/x-pack/plugins/security_solution/public/common/store/actions.ts b/x-pack/plugins/security_solution/public/common/store/actions.ts index 1987edc0e730..585fdb98a032 100644 --- a/x-pack/plugins/security_solution/public/common/store/actions.ts +++ b/x-pack/plugins/security_solution/public/common/store/actions.ts @@ -7,7 +7,6 @@ import { EndpointAction } from '../../management/pages/endpoint_hosts/store/action'; import { PolicyDetailsAction } from '../../management/pages/policy/store/policy_details'; -import { TrustedAppsPageAction } from '../../management/pages/trusted_apps/store/action'; import { EventFiltersPageAction } from '../../management/pages/event_filters/store/action'; export { appActions } from './app'; @@ -20,5 +19,4 @@ export type AppAction = | EndpointAction | RoutingAction | PolicyDetailsAction - | TrustedAppsPageAction | EventFiltersPageAction; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/index.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/index.tsx index 1c0e2144ad9d..6c9a1fca8873 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/index.tsx @@ -43,11 +43,6 @@ export const AlertsCountPanel = memo( const uniqueQueryId = useMemo(() => `${DETECTIONS_ALERTS_COUNT_ID}-${uuid.v4()}`, []); const [selectedStackByOption, setSelectedStackByOption] = useState(DEFAULT_STACK_BY_FIELD); - // TODO: Once we are past experimental phase this code should be removed - // const fetchMethod = useIsExperimentalFeatureEnabled('ruleRegistryEnabled') - // ? fetchQueryRuleRegistryAlerts - // : fetchQueryAlerts; - // Disabling the fecth method in useQueryAlerts since it is defaulted to the old one // const fetchMethod = fetchQueryRuleRegistryAlerts; diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/severity_mapping/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/severity_mapping/index.tsx index 4ea2ece01668..bcb368ba288a 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/severity_mapping/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/severity_mapping/index.tsx @@ -256,7 +256,7 @@ export const SeverityField = ({ { }); const { addError } = useAppToasts(); const { hasIndexRead } = useAlertsPrivileges(); - // TODO: Once we are past experimental phase this code should be removed - const ruleRegistryEnabled = useIsExperimentalFeatureEnabled('ruleRegistryEnabled'); useEffect(() => { let isSubscribed = true; @@ -118,7 +115,7 @@ export const useSignalIndex = (): ReturnSignalIndex => { isSubscribed = false; abortCtrl.abort(); }; - }, [addError, hasIndexRead, ruleRegistryEnabled]); + }, [addError, hasIndexRead]); return { loading, ...signalIndex }; }; diff --git a/x-pack/plugins/security_solution/public/management/common/routing.test.ts b/x-pack/plugins/security_solution/public/management/common/routing.test.ts deleted file mode 100644 index 99afd3068898..000000000000 --- a/x-pack/plugins/security_solution/public/management/common/routing.test.ts +++ /dev/null @@ -1,224 +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 { extractTrustedAppsListPageLocation, getTrustedAppsListPath } from './routing'; -import { MANAGEMENT_DEFAULT_PAGE, MANAGEMENT_DEFAULT_PAGE_SIZE } from './constants'; -import { TrustedAppsListPageLocation } from '../pages/trusted_apps/state'; - -describe('routing', () => { - describe('extractListPaginationParams()', () => { - it('extracts default page index when not provided', () => { - expect(extractTrustedAppsListPageLocation({}).page_index).toBe(MANAGEMENT_DEFAULT_PAGE); - }); - - it('extracts default page index when too small value provided', () => { - expect(extractTrustedAppsListPageLocation({ page_index: '-1' }).page_index).toBe( - MANAGEMENT_DEFAULT_PAGE - ); - }); - - it('extracts default page index when not a number provided', () => { - expect(extractTrustedAppsListPageLocation({ page_index: 'a' }).page_index).toBe( - MANAGEMENT_DEFAULT_PAGE - ); - }); - - it('extracts only last page index when multiple values provided', () => { - expect(extractTrustedAppsListPageLocation({ page_index: ['1', '2'] }).page_index).toBe(2); - }); - - it('extracts proper page index when single valid value provided', () => { - expect(extractTrustedAppsListPageLocation({ page_index: '2' }).page_index).toBe(2); - }); - - it('extracts default page size when not provided', () => { - expect(extractTrustedAppsListPageLocation({}).page_size).toBe(MANAGEMENT_DEFAULT_PAGE_SIZE); - }); - - it('extracts default page size when invalid option provided', () => { - expect(extractTrustedAppsListPageLocation({ page_size: '25' }).page_size).toBe( - MANAGEMENT_DEFAULT_PAGE_SIZE - ); - }); - - it('extracts default page size when not a number provided', () => { - expect(extractTrustedAppsListPageLocation({ page_size: 'a' }).page_size).toBe( - MANAGEMENT_DEFAULT_PAGE_SIZE - ); - }); - - it('extracts only last page size when multiple values provided', () => { - expect(extractTrustedAppsListPageLocation({ page_size: ['10', '20'] }).page_size).toBe(20); - }); - - it('extracts proper page size when single valid value provided', () => { - expect(extractTrustedAppsListPageLocation({ page_size: '20' }).page_size).toBe(20); - }); - - it('extracts proper "show" when single valid value provided', () => { - expect(extractTrustedAppsListPageLocation({ show: 'create' }).show).toBe('create'); - }); - - it('extracts only last "show" when multiple values provided', () => { - expect(extractTrustedAppsListPageLocation({ show: ['invalid', 'create'] }).show).toBe( - 'create' - ); - }); - - it('extracts default "show" when no value provided', () => { - expect(extractTrustedAppsListPageLocation({}).show).toBeUndefined(); - }); - - it('extracts default "show" when single invalid value provided', () => { - expect(extractTrustedAppsListPageLocation({ show: 'invalid' }).show).toBeUndefined(); - }); - - it('extracts proper view type when single valid value provided', () => { - expect(extractTrustedAppsListPageLocation({ view_type: 'list' }).view_type).toBe('list'); - }); - - it('extracts only last view type when multiple values provided', () => { - expect(extractTrustedAppsListPageLocation({ view_type: ['grid', 'list'] }).view_type).toBe( - 'list' - ); - }); - - it('extracts default view type when no value provided', () => { - expect(extractTrustedAppsListPageLocation({}).view_type).toBe('grid'); - }); - - it('extracts default view type when single invalid value provided', () => { - expect(extractTrustedAppsListPageLocation({ view_type: 'invalid' }).view_type).toBe('grid'); - }); - }); - - describe('getTrustedAppsListPath()', () => { - it('builds proper path when no parameters provided', () => { - expect(getTrustedAppsListPath()).toEqual('/administration/trusted_apps'); - }); - - it('builds proper path when empty parameters provided', () => { - expect(getTrustedAppsListPath({})).toEqual('/administration/trusted_apps'); - }); - - it('builds proper path when only page size provided', () => { - const pageSize = 20; - expect(getTrustedAppsListPath({ page_size: pageSize })).toEqual( - `/administration/trusted_apps?page_size=${pageSize}` - ); - }); - - it('builds proper path when only page index provided', () => { - const pageIndex = 2; - expect(getTrustedAppsListPath({ page_index: pageIndex })).toEqual( - `/administration/trusted_apps?page_index=${pageIndex}` - ); - }); - - it('builds proper path when only "show" provided', () => { - const show = 'create'; - expect(getTrustedAppsListPath({ show })).toEqual(`/administration/trusted_apps?show=${show}`); - }); - - it('builds proper path when only view type provided', () => { - const viewType = 'list'; - expect(getTrustedAppsListPath({ view_type: viewType })).toEqual( - `/administration/trusted_apps?view_type=${viewType}` - ); - }); - - it('builds proper path when all params provided', () => { - const location: TrustedAppsListPageLocation = { - page_index: 2, - page_size: 20, - show: 'create', - view_type: 'list', - filter: 'test', - included_policies: 'globally', - }; - - expect(getTrustedAppsListPath(location)).toEqual( - `/administration/trusted_apps?page_index=${location.page_index}&page_size=${location.page_size}&view_type=${location.view_type}&show=${location.show}&filter=${location.filter}&included_policies=${location.included_policies}` - ); - }); - - it('builds proper path when page index is equal to default', () => { - const location: TrustedAppsListPageLocation = { - page_index: MANAGEMENT_DEFAULT_PAGE, - page_size: 20, - show: 'create', - view_type: 'list', - filter: '', - included_policies: '', - }; - const path = getTrustedAppsListPath(location); - - expect(path).toEqual( - `/administration/trusted_apps?page_size=${location.page_size}&view_type=${location.view_type}&show=${location.show}` - ); - }); - - it('builds proper path when page size is equal to default', () => { - const location: TrustedAppsListPageLocation = { - page_index: 2, - page_size: MANAGEMENT_DEFAULT_PAGE_SIZE, - show: 'create', - view_type: 'list', - filter: '', - included_policies: '', - }; - const path = getTrustedAppsListPath(location); - - expect(path).toEqual( - `/administration/trusted_apps?page_index=${location.page_index}&view_type=${location.view_type}&show=${location.show}` - ); - }); - - it('builds proper path when "show" is equal to default', () => { - const location: TrustedAppsListPageLocation = { - page_index: 2, - page_size: 20, - show: undefined, - view_type: 'list', - filter: '', - included_policies: '', - }; - const path = getTrustedAppsListPath(location); - - expect(path).toEqual( - `/administration/trusted_apps?page_index=${location.page_index}&page_size=${location.page_size}&view_type=${location.view_type}` - ); - }); - - it('builds proper path when view type is equal to default', () => { - const location: TrustedAppsListPageLocation = { - page_index: 2, - page_size: 20, - show: 'create', - view_type: 'grid', - filter: '', - included_policies: '', - }; - const path = getTrustedAppsListPath(location); - - expect(path).toEqual( - `/administration/trusted_apps?page_index=${location.page_index}&page_size=${location.page_size}&show=${location.show}` - ); - }); - - it('builds proper path when params are equal to default', () => { - const path = getTrustedAppsListPath({ - page_index: MANAGEMENT_DEFAULT_PAGE, - page_size: MANAGEMENT_DEFAULT_PAGE_SIZE, - show: undefined, - view_type: 'grid', - }); - - expect(path).toEqual('/administration/trusted_apps'); - }); - }); -}); diff --git a/x-pack/plugins/security_solution/public/management/common/routing.ts b/x-pack/plugins/security_solution/public/management/common/routing.ts index 40d17dc76669..828be47d9d0d 100644 --- a/x-pack/plugins/security_solution/public/management/common/routing.ts +++ b/x-pack/plugins/security_solution/public/management/common/routing.ts @@ -16,7 +16,6 @@ import { paginationFromUrlParams } from '../components/hooks/use_url_pagination' import { EndpointIndexUIQueryParams } from '../pages/endpoint_hosts/types'; import { EventFiltersPageLocation } from '../pages/event_filters/types'; import { PolicyDetailsArtifactsPageLocation } from '../pages/policy/types'; -import { TrustedAppsListPageLocation } from '../pages/trusted_apps/state'; import { AdministrationSubTab } from '../types'; import { MANAGEMENT_DEFAULT_PAGE, @@ -150,30 +149,6 @@ export const getPolicyEventFiltersPath = ( )}`; }; -const normalizeTrustedAppsPageLocation = ( - location?: Partial -): Partial => { - if (location) { - return { - ...(!isDefaultOrMissing(location.page_index, MANAGEMENT_DEFAULT_PAGE) - ? { page_index: location.page_index } - : {}), - ...(!isDefaultOrMissing(location.page_size, MANAGEMENT_DEFAULT_PAGE_SIZE) - ? { page_size: location.page_size } - : {}), - ...(!isDefaultOrMissing(location.view_type, 'grid') ? { view_type: location.view_type } : {}), - ...(!isDefaultOrMissing(location.show, undefined) ? { show: location.show } : {}), - ...(!isDefaultOrMissing(location.id, undefined) ? { id: location.id } : {}), - ...(!isDefaultOrMissing(location.filter, '') ? { filter: location.filter } : ''), - ...(!isDefaultOrMissing(location.included_policies, '') - ? { included_policies: location.included_policies } - : ''), - }; - } else { - return {}; - } -}; - const normalizePolicyDetailsArtifactsListPageLocation = ( location?: Partial ): Partial => { @@ -266,31 +241,12 @@ export const extractArtifactsListPaginationParams = (query: querystring.ParsedUr included_policies: extractIncludedPolicies(query), }); -export const extractTrustedAppsListPageLocation = ( - query: querystring.ParsedUrlQuery -): TrustedAppsListPageLocation => { - const showParamValue = extractFirstParamValue( - query, - 'show' - ) as TrustedAppsListPageLocation['show']; - - return { - ...extractTrustedAppsListPaginationParams(query), - view_type: extractFirstParamValue(query, 'view_type') === 'list' ? 'list' : 'grid', - show: - showParamValue && ['edit', 'create'].includes(showParamValue) ? showParamValue : undefined, - id: extractFirstParamValue(query, 'id'), - }; -}; - -export const getTrustedAppsListPath = (location?: Partial): string => { +export const getTrustedAppsListPath = (location?: Partial): string => { const path = generatePath(MANAGEMENT_ROUTING_TRUSTED_APPS_PATH, { tabName: AdministrationSubTab.trustedApps, }); - return `${path}${appendSearch( - querystring.stringify(normalizeTrustedAppsPageLocation(location)) - )}`; + return getArtifactListPageUrlPath(path, location); }; export const extractPolicyDetailsArtifactsListPageLocation = ( diff --git a/x-pack/plugins/security_solution/public/management/common/url_routing/artifact_list_page_routing.test.ts b/x-pack/plugins/security_solution/public/management/common/url_routing/artifact_list_page_routing.test.ts new file mode 100644 index 000000000000..efbed1e2e14f --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/common/url_routing/artifact_list_page_routing.test.ts @@ -0,0 +1,232 @@ +/* + * 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 { ArtifactListPageUrlParams } from '../../components/artifact_list_page'; +import { MANAGEMENT_DEFAULT_PAGE, MANAGEMENT_DEFAULT_PAGE_SIZE } from '../constants'; +import { + getArtifactListPageUrlPath, + extractArtifactListPageUrlSearchParams, +} from './artifact_list_page_routing'; +import { + getTrustedAppsListPath, + getBlocklistsListPath, + getHostIsolationExceptionsListPath, +} from '../routing'; + +describe('routing', () => { + describe('extractListPaginationParams()', () => { + it('extracts default page when not provided', () => { + expect(extractArtifactListPageUrlSearchParams({}).page).toBe(1); + }); + + it('extracts default page when too small value provided', () => { + expect(extractArtifactListPageUrlSearchParams({ page: '-1' }).page).toBe(1); + }); + + it('extracts default page when not a number provided', () => { + expect(extractArtifactListPageUrlSearchParams({ page: 'a' }).page).toBe(1); + }); + + it('extracts only last page when multiple values provided', () => { + expect(extractArtifactListPageUrlSearchParams({ page: ['1', '2'] }).page).toBe(2); + }); + + it('extracts proper page when single valid value provided', () => { + expect(extractArtifactListPageUrlSearchParams({ page: '2' }).page).toBe(2); + }); + + it('extracts default page size when not provided', () => { + expect(extractArtifactListPageUrlSearchParams({}).pageSize).toBe( + MANAGEMENT_DEFAULT_PAGE_SIZE + ); + }); + + it('extracts default page size when invalid option provided', () => { + expect(extractArtifactListPageUrlSearchParams({ pageSize: '25' }).pageSize).toBe( + MANAGEMENT_DEFAULT_PAGE_SIZE + ); + }); + + it('extracts default page size when not a number provided', () => { + expect(extractArtifactListPageUrlSearchParams({ pageSize: 'a' }).pageSize).toBe( + MANAGEMENT_DEFAULT_PAGE_SIZE + ); + }); + + it('extracts only last page size when multiple values provided', () => { + expect(extractArtifactListPageUrlSearchParams({ pageSize: ['10', '20'] }).pageSize).toBe(20); + }); + + it('extracts proper page size when single valid value provided', () => { + expect(extractArtifactListPageUrlSearchParams({ pageSize: '20' }).pageSize).toBe(20); + }); + + it('extracts proper "show" when single valid value provided', () => { + expect(extractArtifactListPageUrlSearchParams({ show: 'create' }).show).toBe('create'); + }); + + it('extracts only last "show" when multiple values provided', () => { + expect(extractArtifactListPageUrlSearchParams({ show: ['invalid', 'create'] }).show).toBe( + 'create' + ); + }); + + it('extracts default "show" when no value provided', () => { + expect(extractArtifactListPageUrlSearchParams({}).show).toBeUndefined(); + }); + + it('extracts default "show" when single invalid value provided', () => { + expect(extractArtifactListPageUrlSearchParams({ show: 'invalid' }).show).toBeUndefined(); + }); + }); + + describe('getArtifactListPageUrlPath()', () => { + it.each([ + ['trusted_apps', getTrustedAppsListPath], + ['blocklist', getBlocklistsListPath], + ['host_isolation_exceptions', getHostIsolationExceptionsListPath], + ])('builds proper path for %s when no parameters provided', (path, getPath) => { + expect(getArtifactListPageUrlPath(getPath())).toEqual(`/administration/${path}`); + }); + + it.each([ + ['trusted_apps', getTrustedAppsListPath], + ['blocklist', getBlocklistsListPath], + ['host_isolation_exceptions', getHostIsolationExceptionsListPath], + ])('builds proper path for %s when only page size provided', (path, getPath) => { + const pageSize = 20; + expect(getArtifactListPageUrlPath(getPath({ pageSize }))).toEqual( + `/administration/${path}?pageSize=${pageSize}` + ); + }); + + it.each([ + ['trusted_apps', getTrustedAppsListPath], + ['blocklist', getBlocklistsListPath], + ['host_isolation_exceptions', getHostIsolationExceptionsListPath], + ])('builds proper path for %s when only page index provided', (path, getPath) => { + const pageIndex = 2; + expect(getArtifactListPageUrlPath(getPath({ page: pageIndex }))).toEqual( + `/administration/${path}?page=${pageIndex}` + ); + }); + + it.each([ + ['trusted_apps', getTrustedAppsListPath], + ['blocklist', getBlocklistsListPath], + ['host_isolation_exceptions', getHostIsolationExceptionsListPath], + ])('builds proper path for %s when only "show" provided', (path, getPath) => { + const show = 'create'; + expect(getArtifactListPageUrlPath(getPath({ show }))).toEqual( + `/administration/${path}?show=${show}` + ); + }); + + it.each([ + ['trusted_apps', getTrustedAppsListPath], + ['blocklist', getBlocklistsListPath], + ['host_isolation_exceptions', getHostIsolationExceptionsListPath], + ])('builds proper path for %s when all params provided', (path, getPath) => { + const location: ArtifactListPageUrlParams = { + page: 2, + pageSize: 20, + show: 'create', + filter: 'test', + includedPolicies: 'globally', + }; + + expect(getPath(location)).toEqual( + `/administration/${path}?page=${location.page}&pageSize=${location.pageSize}&show=${location.show}&filter=${location.filter}&includedPolicies=${location.includedPolicies}` + ); + }); + + it.each([ + ['trusted_apps', getTrustedAppsListPath], + ['blocklist', getBlocklistsListPath], + ['host_isolation_exceptions', getHostIsolationExceptionsListPath], + ])('builds proper path for %s when page index is equal to default', (path, getPath) => { + const location: ArtifactListPageUrlParams = { + page: MANAGEMENT_DEFAULT_PAGE, + pageSize: 20, + show: 'create', + filter: '', + includedPolicies: '', + }; + + expect(getPath(location)).toEqual( + `/administration/${path}?pageSize=${location.pageSize}&show=${location.show}` + ); + }); + + it.each([ + ['trusted_apps', getTrustedAppsListPath], + ['blocklist', getBlocklistsListPath], + ['host_isolation_exceptions', getHostIsolationExceptionsListPath], + ])('builds proper path for %s when page size is equal to default', (path, getPath) => { + const location: ArtifactListPageUrlParams = { + page: 2, + pageSize: MANAGEMENT_DEFAULT_PAGE_SIZE, + show: 'create', + filter: '', + includedPolicies: '', + }; + + expect(getPath(location)).toEqual( + `/administration/${path}?page=${location.page}&show=${location.show}` + ); + }); + + it.each([ + ['trusted_apps', getTrustedAppsListPath], + ['blocklist', getBlocklistsListPath], + ['host_isolation_exceptions', getHostIsolationExceptionsListPath], + ])('builds proper path for %s when "show" is equal to default', (path, getPath) => { + const location: ArtifactListPageUrlParams = { + page: 2, + pageSize: 20, + show: undefined, + filter: '', + includedPolicies: '', + }; + + expect(getPath(location)).toEqual( + `/administration/${path}?page=${location.page}&pageSize=${location.pageSize}` + ); + }); + + it.each([ + ['trusted_apps', getTrustedAppsListPath], + ['blocklist', getBlocklistsListPath], + ['host_isolation_exceptions', getHostIsolationExceptionsListPath], + ])('builds proper path for %s when view type is equal to default', (path, getPath) => { + const location: ArtifactListPageUrlParams = { + page: 2, + pageSize: 20, + show: 'create', + filter: '', + includedPolicies: '', + }; + + expect(getPath(location)).toEqual( + `/administration/${path}?page=${location.page}&pageSize=${location.pageSize}&show=${location.show}` + ); + }); + + it.each([ + ['trusted_apps', getTrustedAppsListPath], + ['blocklist', getBlocklistsListPath], + ['host_isolation_exceptions', getHostIsolationExceptionsListPath], + ])('builds proper path for %s when params are equal to default', (path, getPath) => { + const location: ArtifactListPageUrlParams = { + page: MANAGEMENT_DEFAULT_PAGE, + pageSize: MANAGEMENT_DEFAULT_PAGE_SIZE, + }; + + expect(getPath(location)).toEqual(`/administration/${path}`); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/management/common/url_routing/utils.ts b/x-pack/plugins/security_solution/public/management/common/url_routing/utils.ts index b9ec8b2fb7a8..afff61e94b06 100644 --- a/x-pack/plugins/security_solution/public/management/common/url_routing/utils.ts +++ b/x-pack/plugins/security_solution/public/management/common/url_routing/utils.ts @@ -14,8 +14,8 @@ import { MANAGEMENT_DEFAULT_PAGE_SIZE, MANAGEMENT_PAGE_SIZE_OPTIONS } from '../c * @param value * @param defaultValue */ -export const isDefaultOrMissing = (value: T | undefined, defaultValue: T) => { - return value === undefined || value === defaultValue; +export const isDefaultOrMissing = (value: T | undefined | 0, defaultValue: T) => { + return value === undefined || value === defaultValue || value === 0; }; /** diff --git a/x-pack/plugins/security_solution/public/management/components/artifact_list_page/components/artifact_flyout.test.tsx b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/components/artifact_flyout.test.tsx index 81dc91dbae32..b21579d25ded 100644 --- a/x-pack/plugins/security_solution/public/management/components/artifact_list_page/components/artifact_flyout.test.tsx +++ b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/components/artifact_flyout.test.tsx @@ -16,6 +16,8 @@ import { getEndpointPrivilegesInitialStateMock } from '../../../../common/compon import { AppContextTestRender } from '../../../../common/mock/endpoint'; import { trustedAppsAllHttpMocks } from '../../../pages/mocks'; import { useUserPrivileges as _useUserPrivileges } from '../../../../common/components/user_privileges'; +import { entriesToConditionEntries } from '../../../../common/utils/exception_list_items/mappers'; +import { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; jest.mock('../../../../common/components/user_privileges'); const useUserPrivileges = _useUserPrivileges as jest.Mock; @@ -357,12 +359,19 @@ describe('When the flyout is opened in the ArtifactListPage component', () => { }); }); - expect(getLastFormComponentProps().item).toEqual({ + const expectedProps = { ...mockedApi.responseProvider.trustedApp({ query: { item_id: '123' }, } as unknown as HttpFetchOptionsWithPath), created_at: expect.any(String), - }); + }; + + // map process.hash entries to have * as suffix + expectedProps.entries = entriesToConditionEntries( + expectedProps.entries + ) as ExceptionListItemSchema['entries']; + + expect(getLastFormComponentProps().item).toEqual(expectedProps); }); it('should show error toast and close flyout if item for edit does not exist', async () => { diff --git a/x-pack/plugins/security_solution/public/management/components/artifact_list_page/mocks.tsx b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/mocks.tsx index d4f92bd2717f..ebf98c64d7dc 100644 --- a/x-pack/plugins/security_solution/public/management/components/artifact_list_page/mocks.tsx +++ b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/mocks.tsx @@ -14,7 +14,7 @@ import { ArtifactFormComponentProps } from './types'; import { ArtifactListPage, ArtifactListPageProps } from './artifact_list_page'; import { AppContextTestRender, createAppRootMockRenderer } from '../../../common/mock/endpoint'; import { trustedAppsAllHttpMocks } from '../../pages/mocks'; -import { TrustedAppsApiClient } from '../../pages/trusted_apps/service/trusted_apps_api_client'; +import { TrustedAppsApiClient } from '../../pages/trusted_apps/service/api_client'; import { artifactListPageLabels } from './translations'; export const getFormComponentMock = (): { diff --git a/x-pack/plugins/security_solution/public/management/pages/blocklist/view/components/blocklist_form.tsx b/x-pack/plugins/security_solution/public/management/pages/blocklist/view/components/blocklist_form.tsx index 3926d42fb36c..ccb2ec9055fc 100644 --- a/x-pack/plugins/security_solution/public/management/pages/blocklist/view/components/blocklist_form.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/blocklist/view/components/blocklist_form.tsx @@ -61,7 +61,7 @@ import { BY_POLICY_ARTIFACT_TAG_PREFIX, } from '../../../../../../common/endpoint/service/artifacts/constants'; import { useLicense } from '../../../../../common/hooks/use_license'; -import { isValidHash } from '../../../../../../common/endpoint/service/trusted_apps/validations'; +import { isValidHash } from '../../../../../../common/endpoint/service/artifacts/validations'; import { isArtifactGlobal } from '../../../../../../common/endpoint/service/artifacts'; import type { PolicyData } from '../../../../../../common/endpoint/types'; import { isGlobalPolicyEffected } from '../../../../components/effected_policy_select/utils'; diff --git a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/form/index.test.tsx b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/form/index.test.tsx index a9778329ff2f..f0589099a807 100644 --- a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/form/index.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/form/index.test.tsx @@ -87,6 +87,7 @@ describe('Event filter form', () => { services: { http: {}, data: {}, + unifiedSearch: {}, notifications: {}, }, }); diff --git a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/form/index.tsx b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/form/index.tsx index 0bd785a28eff..11d1af0a5a2e 100644 --- a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/form/index.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/form/index.tsx @@ -88,7 +88,7 @@ interface EventFiltersFormProps { } export const EventFiltersForm: React.FC = memo( ({ allowSelectOs = false, policies, arePoliciesLoading }) => { - const { http, data } = useKibana().services; + const { http, unifiedSearch } = useKibana().services; const dispatch = useDispatch>(); const exception = useEventFiltersSelector(getFormEntryStateMutable); @@ -225,7 +225,7 @@ export const EventFiltersForm: React.FC = memo( getExceptionBuilderComponentLazy({ allowLargeValueLists: false, httpService: http, - autocompleteService: data.autocomplete, + autocompleteService: unifiedSearch.autocomplete, exceptionListItems: [exception as ExceptionListItemSchema], listType: EVENT_FILTER_LIST_TYPE, listId: ENDPOINT_EVENT_FILTERS_LIST_ID, @@ -243,7 +243,7 @@ export const EventFiltersForm: React.FC = memo( operatorsList: EVENT_FILTERS_OPERATORS, osTypes: exception?.os_types, }), - [data, handleOnBuilderChange, http, indexPatterns, exception] + [unifiedSearch, handleOnBuilderChange, http, indexPatterns, exception] ); const nameInputMemo = useMemo( diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/test_utils/mocks.ts b/x-pack/plugins/security_solution/public/management/pages/policy/test_utils/mocks.ts index c1b30b6fad10..e3641ba66e15 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/test_utils/mocks.ts +++ b/x-pack/plugins/security_solution/public/management/pages/policy/test_utils/mocks.ts @@ -5,8 +5,61 @@ * 2.0. */ -import { GetTrustedAppsListResponse } from '../../../../../common/endpoint/types'; -import { createSampleTrustedApps } from '../../trusted_apps/test_utils'; +import { OperatingSystem } from '@kbn/securitysolution-utils'; +import { GetTrustedAppsListResponse, TrustedApp } from '../../../../../common/endpoint/types'; +import { + MANAGEMENT_DEFAULT_PAGE, + MANAGEMENT_DEFAULT_PAGE_SIZE, + MANAGEMENT_PAGE_SIZE_OPTIONS, +} from '../../../common/constants'; + +interface Pagination { + pageIndex: number; + pageSize: number; + totalItemCount: number; + pageSizeOptions: number[]; +} + +const OPERATING_SYSTEMS: OperatingSystem[] = [ + OperatingSystem.WINDOWS, + OperatingSystem.MAC, + OperatingSystem.LINUX, +]; + +const generate = (count: number, generator: (i: number) => T) => + [...new Array(count).keys()].map(generator); + +const createSampleTrustedApp = (i: number, longTexts?: boolean): TrustedApp => { + return { + id: String(i), + version: 'abc123', + name: generate(longTexts ? 10 : 1, () => `trusted app ${i}`).join(' '), + description: generate(longTexts ? 10 : 1, () => `Trusted App ${i}`).join(' '), + created_at: '1 minute ago', + created_by: 'someone', + updated_at: '1 minute ago', + updated_by: 'someone', + os: OPERATING_SYSTEMS[i % 3], + entries: [], + effectScope: { type: 'global' }, + }; +}; + +const createDefaultPagination = (): Pagination => ({ + pageIndex: MANAGEMENT_DEFAULT_PAGE, + pageSize: MANAGEMENT_DEFAULT_PAGE_SIZE, + totalItemCount: 200, + pageSizeOptions: [...MANAGEMENT_PAGE_SIZE_OPTIONS], +}); + +const createSampleTrustedApps = ( + pagination: Partial, + longTexts?: boolean +): TrustedApp[] => { + const fullPagination = { ...createDefaultPagination(), ...pagination }; + + return generate(fullPagination.pageSize, (i: number) => createSampleTrustedApp(i, longTexts)); +}; export const getMockListResponse: () => GetTrustedAppsListResponse = () => ({ data: createSampleTrustedApps({}), diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/artifacts/empty/policy_artifacts_empty_unassigned.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/artifacts/empty/policy_artifacts_empty_unassigned.tsx index 4809ee53b0d7..2bee159dc6e4 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/artifacts/empty/policy_artifacts_empty_unassigned.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/artifacts/empty/policy_artifacts_empty_unassigned.tsx @@ -12,7 +12,6 @@ import { useGetLinkTo } from './use_policy_artifacts_empty_hooks'; import { useUserPrivileges } from '../../../../../../common/components/user_privileges'; import { POLICY_ARTIFACT_EMPTY_UNASSIGNED_LABELS } from './translations'; import { EventFiltersPageLocation } from '../../../../event_filters/types'; -import { TrustedAppsListPageLocation } from '../../../../trusted_apps/state'; import { ArtifactListPageUrlParams } from '../../../../../components/artifact_list_page'; interface CommonProps { policyId: string; @@ -21,10 +20,7 @@ interface CommonProps { labels: typeof POLICY_ARTIFACT_EMPTY_UNASSIGNED_LABELS; getPolicyArtifactsPath: (policyId: string) => string; getArtifactPath: ( - location?: - | Partial - | Partial - | Partial + location?: Partial | Partial ) => string; } diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/artifacts/empty/policy_artifacts_empty_unexisting.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/artifacts/empty/policy_artifacts_empty_unexisting.tsx index eb0133823ea9..7d1238904875 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/artifacts/empty/policy_artifacts_empty_unexisting.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/artifacts/empty/policy_artifacts_empty_unexisting.tsx @@ -10,7 +10,6 @@ import { EuiEmptyPrompt, EuiButton, EuiPageTemplate } from '@elastic/eui'; import { useGetLinkTo } from './use_policy_artifacts_empty_hooks'; import { POLICY_ARTIFACT_EMPTY_UNEXISTING_LABELS } from './translations'; import { EventFiltersPageLocation } from '../../../../event_filters/types'; -import { TrustedAppsListPageLocation } from '../../../../trusted_apps/state'; import { ArtifactListPageUrlParams } from '../../../../../components/artifact_list_page'; interface CommonProps { @@ -19,10 +18,7 @@ interface CommonProps { labels: typeof POLICY_ARTIFACT_EMPTY_UNEXISTING_LABELS; getPolicyArtifactsPath: (policyId: string) => string; getArtifactPath: ( - location?: - | Partial - | Partial - | Partial + location?: Partial | Partial ) => string; } diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/artifacts/empty/use_policy_artifacts_empty_hooks.ts b/x-pack/plugins/security_solution/public/management/pages/policy/view/artifacts/empty/use_policy_artifacts_empty_hooks.ts index 723988a9de46..b2ee8e7dd17b 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/artifacts/empty/use_policy_artifacts_empty_hooks.ts +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/artifacts/empty/use_policy_artifacts_empty_hooks.ts @@ -11,7 +11,6 @@ import { useNavigateToAppEventHandler } from '../../../../../../common/hooks/end import { useAppUrl } from '../../../../../../common/lib/kibana/hooks'; import { APP_UI_ID } from '../../../../../../../common/constants'; import { EventFiltersPageLocation } from '../../../../event_filters/types'; -import { TrustedAppsListPageLocation } from '../../../../trusted_apps/state'; import { ArtifactListPageUrlParams } from '../../../../../components/artifact_list_page'; export const useGetLinkTo = ( @@ -19,10 +18,7 @@ export const useGetLinkTo = ( policyName: string, getPolicyArtifactsPath: (policyId: string) => string, getArtifactPath: ( - location?: - | Partial - | Partial - | Partial + location?: Partial | Partial ) => string, location?: Partial<{ show: 'create' }> ) => { diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/artifacts/layout/policy_artifacts_layout.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/artifacts/layout/policy_artifacts_layout.tsx index 92b0edc690a4..ac3cf9e536b9 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/artifacts/layout/policy_artifacts_layout.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/artifacts/layout/policy_artifacts_layout.tsx @@ -32,7 +32,6 @@ import { PolicyArtifactsFlyout } from '../flyout'; import { PolicyArtifactsPageLabels, policyArtifactsPageLabels } from '../translations'; import { PolicyArtifactsDeleteModal } from '../delete_modal'; import { EventFiltersPageLocation } from '../../../../event_filters/types'; -import { TrustedAppsListPageLocation } from '../../../../trusted_apps/state'; import { ArtifactListPageUrlParams } from '../../../../../components/artifact_list_page'; interface PolicyArtifactsLayoutProps { @@ -42,10 +41,7 @@ interface PolicyArtifactsLayoutProps { getExceptionsListApiClient: () => ExceptionsListApiClient; searchableFields: readonly string[]; getArtifactPath: ( - location?: - | Partial - | Partial - | Partial + location?: Partial | Partial ) => string; getPolicyArtifactsPath: (policyId: string) => string; /** A boolean to check extra privileges for restricted actions, true when it's allowed, false when not */ diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/artifacts/list/policy_artifacts_list.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/artifacts/list/policy_artifacts_list.tsx index 863b7de4a481..445725915899 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/artifacts/list/policy_artifacts_list.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/artifacts/list/policy_artifacts_list.tsx @@ -29,7 +29,6 @@ import { ExceptionsListApiClient } from '../../../../../services/exceptions_list import { useListArtifact } from '../../../../../hooks/artifacts'; import { POLICY_ARTIFACT_LIST_LABELS } from './translations'; import { EventFiltersPageLocation } from '../../../../event_filters/types'; -import { TrustedAppsListPageLocation } from '../../../../trusted_apps/state'; import { ArtifactListPageUrlParams } from '../../../../../components/artifact_list_page'; interface PolicyArtifactsListProps { @@ -37,10 +36,7 @@ interface PolicyArtifactsListProps { apiClient: ExceptionsListApiClient; searchableFields: string[]; getArtifactPath: ( - location?: - | Partial - | Partial - | Partial + location?: Partial | Partial ) => string; getPolicyArtifactsPath: (policyId: string) => string; labels: typeof POLICY_ARTIFACT_LIST_LABELS; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/endpoint_package_custom_extension.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/endpoint_package_custom_extension.tsx index e6984087bef5..7700c1f71fba 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/endpoint_package_custom_extension.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/endpoint_package_custom_extension.tsx @@ -10,7 +10,7 @@ import { EuiSpacer } from '@elastic/eui'; import { PackageCustomExtensionComponentProps } from '@kbn/fleet-plugin/public'; import { useHttp } from '../../../../../../common/lib/kibana'; import { useCanSeeHostIsolationExceptionsMenu } from '../../../../host_isolation_exceptions/view/hooks'; -import { TrustedAppsApiClient } from '../../../../trusted_apps/service/trusted_apps_api_client'; +import { TrustedAppsApiClient } from '../../../../trusted_apps/service/api_client'; import { EventFiltersApiClient } from '../../../../event_filters/service/event_filters_api_client'; import { HostIsolationExceptionsApiClient } from '../../../../host_isolation_exceptions/host_isolation_exceptions_api_client'; import { BlocklistsApiClient } from '../../../../blocklist/services'; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_policy_edit_extension.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_policy_edit_extension.tsx index 87537014e6b0..dfb2677ecb59 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_policy_edit_extension.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_policy_edit_extension.tsx @@ -36,7 +36,7 @@ import { FleetIntegrationArtifactsCard } from './endpoint_package_custom_extensi import { BlocklistsApiClient } from '../../../blocklist/services'; import { HostIsolationExceptionsApiClient } from '../../../host_isolation_exceptions/host_isolation_exceptions_api_client'; import { EventFiltersApiClient } from '../../../event_filters/service/event_filters_api_client'; -import { TrustedAppsApiClient } from '../../../trusted_apps/service/trusted_apps_api_client'; +import { TrustedAppsApiClient } from '../../../trusted_apps/service/api_client'; import { SEARCHABLE_FIELDS as BLOCKLIST_SEARCHABLE_FIELDS } from '../../../blocklist/constants'; import { SEARCHABLE_FIELDS as HOST_ISOLATION_EXCEPTIONS_SEARCHABLE_FIELDS } from '../../../host_isolation_exceptions/constants'; import { SEARCHABLE_FIELDS as EVENT_FILTERS_SEARCHABLE_FIELDS } from '../../../event_filters/constants'; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/tabs/policy_tabs.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/tabs/policy_tabs.tsx index 62778bb164d9..f3a20a1abfd6 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/tabs/policy_tabs.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/tabs/policy_tabs.tsx @@ -41,7 +41,7 @@ import { POLICY_ARTIFACT_EVENT_FILTERS_LABELS } from './event_filters_translatio import { POLICY_ARTIFACT_TRUSTED_APPS_LABELS } from './trusted_apps_translations'; import { POLICY_ARTIFACT_HOST_ISOLATION_EXCEPTIONS_LABELS } from './host_isolation_exceptions_translations'; import { POLICY_ARTIFACT_BLOCKLISTS_LABELS } from './blocklists_translations'; -import { TrustedAppsApiClient } from '../../../trusted_apps/service/trusted_apps_api_client'; +import { TrustedAppsApiClient } from '../../../trusted_apps/service/api_client'; import { EventFiltersApiClient } from '../../../event_filters/service/event_filters_api_client'; import { BlocklistsApiClient } from '../../../blocklist/services/blocklists_api_client'; import { HostIsolationExceptionsApiClient } from '../../../host_isolation_exceptions/host_isolation_exceptions_api_client'; diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/index.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/index.tsx index 018987c81b94..d9edd9986e15 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/index.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/index.tsx @@ -6,16 +6,18 @@ */ import { Switch, Route } from 'react-router-dom'; -import React from 'react'; -import { TrustedAppsPage } from './view'; +import React, { memo } from 'react'; +import { TrustedAppsList } from './view/trusted_apps_list'; import { MANAGEMENT_ROUTING_TRUSTED_APPS_PATH } from '../../common/constants'; import { NotFoundPage } from '../../../app/404'; -export function TrustedAppsContainer() { +export const TrustedAppsContainer = memo(() => { return ( - + ); -} +}); + +TrustedAppsContainer.displayName = 'TrustedAppsContainer'; diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/service/api_client.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/service/api_client.ts new file mode 100644 index 000000000000..e66c16717e42 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/service/api_client.ts @@ -0,0 +1,65 @@ +/* + * 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 { + CreateExceptionListItemSchema, + ExceptionListItemSchema, + UpdateExceptionListItemSchema, +} from '@kbn/securitysolution-io-ts-list-types'; +import { ENDPOINT_TRUSTED_APPS_LIST_ID } from '@kbn/securitysolution-list-constants'; + +import { HttpStart } from '@kbn/core/public'; +import { ConditionEntry } from '../../../../../common/endpoint/types'; +import { + conditionEntriesToEntries, + entriesToConditionEntries, +} from '../../../../common/utils/exception_list_items'; +import { ExceptionsListApiClient } from '../../../services/exceptions_list/exceptions_list_api_client'; +import { TRUSTED_APPS_EXCEPTION_LIST_DEFINITION } from '../constants'; + +function readTransform(item: ExceptionListItemSchema): ExceptionListItemSchema { + return { + ...item, + entries: entriesToConditionEntries(item.entries) as ExceptionListItemSchema['entries'], + }; +} + +function writeTransform( + item: T +): T { + return { + ...item, + entries: conditionEntriesToEntries(item.entries as ConditionEntry[], true), + } as T; +} + +/** + * Trusted Apps exceptions Api client class using ExceptionsListApiClient as base class + * It follows the Singleton pattern. + * Please, use the getInstance method instead of creating a new instance when using this implementation. + */ +export class TrustedAppsApiClient extends ExceptionsListApiClient { + constructor(http: HttpStart) { + super( + http, + ENDPOINT_TRUSTED_APPS_LIST_ID, + TRUSTED_APPS_EXCEPTION_LIST_DEFINITION, + readTransform, + writeTransform + ); + } + + public static getInstance(http: HttpStart): ExceptionsListApiClient { + return super.getInstance( + http, + ENDPOINT_TRUSTED_APPS_LIST_ID, + TRUSTED_APPS_EXCEPTION_LIST_DEFINITION, + readTransform, + writeTransform + ); + } +} diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/service/index.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/service/index.ts index 3c0d58043dd0..4b2dee13ce70 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/service/index.ts +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/service/index.ts @@ -5,4 +5,4 @@ * 2.0. */ -export * from './trusted_apps_http_service'; +export { TrustedAppsApiClient } from './api_client'; diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/service/trusted_apps_api_client.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/service/trusted_apps_api_client.ts deleted file mode 100644 index 574f32dca6b3..000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/service/trusted_apps_api_client.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 { ENDPOINT_TRUSTED_APPS_LIST_ID } from '@kbn/securitysolution-list-constants'; -import { HttpStart } from '@kbn/core/public'; -import { ExceptionsListApiClient } from '../../../services/exceptions_list/exceptions_list_api_client'; -import { TRUSTED_APPS_EXCEPTION_LIST_DEFINITION } from '../constants'; - -/** - * Trusted apps Api client class using ExceptionsListApiClient as base class - * It follow the Singleton pattern. - * Please, use the getInstance method instead of creating a new instance when using this implementation. - */ -export class TrustedAppsApiClient extends ExceptionsListApiClient { - constructor(http: HttpStart) { - super(http, ENDPOINT_TRUSTED_APPS_LIST_ID, TRUSTED_APPS_EXCEPTION_LIST_DEFINITION); - } - - public static getInstance(http: HttpStart): ExceptionsListApiClient { - return super.getInstance( - http, - ENDPOINT_TRUSTED_APPS_LIST_ID, - TRUSTED_APPS_EXCEPTION_LIST_DEFINITION - ); - } -} diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/service/trusted_apps_http_service.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/service/trusted_apps_http_service.ts deleted file mode 100644 index a16ccd9169db..000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/service/trusted_apps_http_service.ts +++ /dev/null @@ -1,288 +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 { - ExceptionListItemSchema, - ExceptionListSchema, - ExceptionListSummarySchema, - FoundExceptionListItemSchema, -} from '@kbn/securitysolution-io-ts-list-types'; -import { - ENDPOINT_TRUSTED_APPS_LIST_ID, - EXCEPTION_LIST_ITEM_URL, - EXCEPTION_LIST_URL, -} from '@kbn/securitysolution-list-constants'; -import { HttpStart } from '@kbn/core/public'; -import pMap from 'p-map'; -import { toUpdateTrustedApp } from '../../../../../common/endpoint/service/trusted_apps/to_update_trusted_app'; -import { - DeleteTrustedAppsRequestParams, - GetOneTrustedAppRequestParams, - GetOneTrustedAppResponse, - GetTrustedAppsListRequest, - GetTrustedAppsListResponse, - MaybeImmutable, - PostTrustedAppCreateRequest, - PostTrustedAppCreateResponse, - PutTrustedAppsRequestParams, - PutTrustedAppUpdateRequest, - PutTrustedAppUpdateResponse, - TrustedApp, -} from '../../../../../common/endpoint/types'; -import { sendGetEndpointSpecificPackagePolicies } from '../../../services/policies/policies'; -import { TRUSTED_APPS_EXCEPTION_LIST_DEFINITION } from '../constants'; -import { isGlobalEffectScope } from '../state/type_guards'; -import { - exceptionListItemToTrustedApp, - newTrustedAppToCreateExceptionListItem, - updatedTrustedAppToUpdateExceptionListItem, -} from './mappers'; -import { validateTrustedAppHttpRequestBody } from './validate_trusted_app_http_request_body'; - -export interface TrustedAppsService { - getTrustedApp(params: GetOneTrustedAppRequestParams): Promise; - - getTrustedAppsList(request: GetTrustedAppsListRequest): Promise; - - deleteTrustedApp(request: DeleteTrustedAppsRequestParams): Promise; - - createTrustedApp(request: PostTrustedAppCreateRequest): Promise; - - updateTrustedApp( - params: PutTrustedAppsRequestParams, - request: PutTrustedAppUpdateRequest - ): Promise; - - getPolicyList( - options?: Parameters[1] - ): ReturnType; - - assignPolicyToTrustedApps( - policyId: string, - trustedApps: MaybeImmutable - ): Promise; - - removePolicyFromTrustedApps( - policyId: string, - trustedApps: MaybeImmutable - ): Promise; -} - -const P_MAP_OPTIONS = Object.freeze({ - concurrency: 5, - /** When set to false, instead of stopping when a promise rejects, it will wait for all the promises to settle - * and then reject with an aggregated error containing all the errors from the rejected promises. */ - stopOnError: false, -}); - -export class TrustedAppsHttpService implements TrustedAppsService { - private readonly getHttpService: () => Promise; - - constructor(http: HttpStart) { - let ensureListExists: Promise; - - this.getHttpService = async () => { - if (!ensureListExists) { - ensureListExists = http - .post(EXCEPTION_LIST_URL, { - body: JSON.stringify(TRUSTED_APPS_EXCEPTION_LIST_DEFINITION), - }) - .then(() => {}) - .catch((err) => { - if (err.response.status !== 409) { - return Promise.reject(err); - } - }); - } - - await ensureListExists; - return http; - }; - } - - private async getExceptionListItem(itemId: string): Promise { - return (await this.getHttpService()).get(EXCEPTION_LIST_ITEM_URL, { - query: { - item_id: itemId, - namespace_type: 'agnostic', - }, - }); - } - - async getTrustedApp(params: GetOneTrustedAppRequestParams) { - const exceptionItem = await this.getExceptionListItem(params.id); - - return { - data: exceptionListItemToTrustedApp(exceptionItem), - }; - } - - async getTrustedAppsList({ - page = 1, - per_page: perPage = 10, - kuery, - }: GetTrustedAppsListRequest): Promise { - const itemListResults = await ( - await this.getHttpService() - ).get(`${EXCEPTION_LIST_ITEM_URL}/_find`, { - query: { - page, - per_page: perPage, - filter: kuery, - sort_field: 'name', - sort_order: 'asc', - list_id: [ENDPOINT_TRUSTED_APPS_LIST_ID], - namespace_type: ['agnostic'], - }, - }); - - return { - ...itemListResults, - data: itemListResults.data.map(exceptionListItemToTrustedApp), - }; - } - - async deleteTrustedApp(request: DeleteTrustedAppsRequestParams): Promise { - await ( - await this.getHttpService() - ).delete(EXCEPTION_LIST_ITEM_URL, { - query: { - item_id: request.id, - namespace_type: 'agnostic', - }, - }); - } - - async createTrustedApp(request: PostTrustedAppCreateRequest) { - await validateTrustedAppHttpRequestBody(await this.getHttpService(), request); - - const newTrustedAppException = newTrustedAppToCreateExceptionListItem(request); - const createdExceptionItem = await ( - await this.getHttpService() - ).post(EXCEPTION_LIST_ITEM_URL, { - body: JSON.stringify(newTrustedAppException), - }); - - return { - data: exceptionListItemToTrustedApp(createdExceptionItem), - }; - } - - async updateTrustedApp( - params: PutTrustedAppsRequestParams, - updatedTrustedApp: PutTrustedAppUpdateRequest - ) { - const [currentExceptionListItem] = await Promise.all([ - await this.getExceptionListItem(params.id), - await validateTrustedAppHttpRequestBody(await this.getHttpService(), updatedTrustedApp), - ]); - - const updatedExceptionListItem = await ( - await this.getHttpService() - ).put(EXCEPTION_LIST_ITEM_URL, { - body: JSON.stringify( - updatedTrustedAppToUpdateExceptionListItem(currentExceptionListItem, updatedTrustedApp) - ), - }); - - return { - data: exceptionListItemToTrustedApp(updatedExceptionListItem), - }; - } - - async getTrustedAppsSummary(filter?: string) { - return (await this.getHttpService()).get( - `${EXCEPTION_LIST_URL}/summary`, - { - query: { - filter, - list_id: ENDPOINT_TRUSTED_APPS_LIST_ID, - namespace_type: 'agnostic', - }, - } - ); - } - - async getPolicyList(options?: Parameters[1]) { - return sendGetEndpointSpecificPackagePolicies(await this.getHttpService(), options); - } - - /** - * Assign a policy to trusted apps. Note that Trusted Apps MUST NOT be global - * - * @param policyId - * @param trustedApps[] - */ - assignPolicyToTrustedApps( - policyId: string, - trustedApps: MaybeImmutable - ): Promise { - return this._handleAssignOrRemovePolicyId('assign', policyId, trustedApps); - } - - /** - * Remove a policy from trusted apps. Note that Trusted Apps MUST NOT be global - * - * @param policyId - * @param trustedApps[] - */ - removePolicyFromTrustedApps( - policyId: string, - trustedApps: MaybeImmutable - ): Promise { - return this._handleAssignOrRemovePolicyId('remove', policyId, trustedApps); - } - - private _handleAssignOrRemovePolicyId( - action: 'assign' | 'remove', - policyId: string, - trustedApps: MaybeImmutable - ): Promise { - if (policyId.trim() === '') { - throw new Error('policy ID is required'); - } - - if (trustedApps.length === 0) { - throw new Error('at least one trusted app is required'); - } - - return pMap( - trustedApps, - async (trustedApp) => { - if (isGlobalEffectScope(trustedApp.effectScope)) { - throw new Error( - `Unable to update trusted app [${trustedApp.id}] policy assignment. It's effectScope is 'global'` - ); - } - - const policies: string[] = !isGlobalEffectScope(trustedApp.effectScope) - ? [...trustedApp.effectScope.policies] - : []; - - const indexOfPolicyId = policies.indexOf(policyId); - - if (action === 'assign' && indexOfPolicyId === -1) { - policies.push(policyId); - } else if (action === 'remove' && indexOfPolicyId !== -1) { - policies.splice(indexOfPolicyId, 1); - } - - return this.updateTrustedApp( - { id: trustedApp.id }, - { - ...toUpdateTrustedApp(trustedApp), - effectScope: { - type: 'policy', - policies, - }, - } - ); - }, - P_MAP_OPTIONS - ); - } -} diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/service/validate_trusted_app_http_request_body.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/service/validate_trusted_app_http_request_body.ts deleted file mode 100644 index 69b281e88fc9..000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/service/validate_trusted_app_http_request_body.ts +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { HttpStart } from '@kbn/core/public'; -import { AGENT_POLICY_SAVED_OBJECT_TYPE } from '@kbn/fleet-plugin/common'; -import { - PostTrustedAppCreateRequest, - PutTrustedAppUpdateRequest, -} from '../../../../../common/endpoint/types'; -import { HttpRequestValidationError } from './errors'; -import { sendGetAgentPolicyList } from '../../../services/policies/ingest'; - -/** - * Validates that the Trusted App is valid for sending to the API (`POST` + 'PUT') - * - * @throws - */ -export const validateTrustedAppHttpRequestBody = async ( - http: HttpStart, - trustedApp: PostTrustedAppCreateRequest | PutTrustedAppUpdateRequest -): Promise => { - const failedValidations: string[] = []; - - // Validate that the Policy IDs are still valid - if (trustedApp.effectScope.type === 'policy' && trustedApp.effectScope.policies.length) { - const policyIds = trustedApp.effectScope.policies; - - // We can't search against the Package Policy API by ID because there is no way to do that. - // So, as a work-around, we use the Agent Policy API and check for those Agent Policies that - // have these package policies in it. For endpoint, these are 1-to-1. - const agentPoliciesFound = await sendGetAgentPolicyList(http, { - query: { - kuery: `${AGENT_POLICY_SAVED_OBJECT_TYPE}.package_policies: (${policyIds.join(' or ')})`, - }, - }); - - if (!agentPoliciesFound.items.length) { - failedValidations.push(`Invalid Policy Id(s) [${policyIds.join(', ')}]`); - } else { - const missingPolicies = policyIds.filter( - (policyId) => - !agentPoliciesFound.items.find(({ package_policies: packagePolicies }) => - (packagePolicies as string[]).includes(policyId) - ) - ); - - if (missingPolicies.length) { - failedValidations.push(`Invalid Policy Id(s) [${missingPolicies.join(', ')}]`); - } - } - } - - if (failedValidations.length) { - throw new HttpRequestValidationError(failedValidations); - } -}; diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/state/trusted_apps_list_page_state.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/state/trusted_apps_list_page_state.ts deleted file mode 100644 index 292b982eebb4..000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/state/trusted_apps_list_page_state.ts +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { NewTrustedApp, TrustedApp } from '../../../../../common/endpoint/types/trusted_apps'; -import { AsyncResourceState } from '../../../state/async_resource_state'; -import { GetPolicyListResponse } from '../../policy/types'; - -export interface Pagination { - pageIndex: number; - pageSize: number; - totalItemCount: number; - pageSizeOptions: number[]; -} - -export interface TrustedAppsListData { - items: TrustedApp[]; - pageIndex: number; - pageSize: number; - timestamp: number; - totalItemsCount: number; - filter: string; - includedPolicies: string; -} - -export type ViewType = 'list' | 'grid'; - -export interface TrustedAppsListPageLocation { - page_index: number; - page_size: number; - view_type: ViewType; - show?: 'create' | 'edit'; - /** Used for editing. The ID of the selected trusted app */ - id?: string; - filter: string; - // A string with comma dlimetered list of included policy IDs - included_policies: string; -} - -export interface TrustedAppsListPageState { - /** Represents if trusted apps entries exist, regardless of whether the list is showing results - * or not (which could use filtering in the future) - */ - entriesExist: AsyncResourceState; - listView: { - listResourceState: AsyncResourceState; - freshDataTimestamp: number; - }; - deletionDialog: { - entry?: TrustedApp; - confirmed: boolean; - submissionResourceState: AsyncResourceState; - }; - creationDialog: { - formState?: { - entry: NewTrustedApp; - isValid: boolean; - }; - /** The trusted app to be edited (when in edit mode) */ - editItem?: AsyncResourceState; - confirmed: boolean; - submissionResourceState: AsyncResourceState; - }; - /** A list of all available polices for use in associating TA to policies */ - policies: AsyncResourceState; - location: TrustedAppsListPageLocation; - active: boolean; - forceRefresh: boolean; -} diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/state/type_guards.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/state/type_guards.ts index 1a28e6f3bfec..239255b641bf 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/state/type_guards.ts +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/state/type_guards.ts @@ -8,11 +8,7 @@ import { ConditionEntryField } from '@kbn/securitysolution-utils'; import { TrustedAppConditionEntry, - EffectScope, - GlobalEffectScope, MacosLinuxConditionEntry, - MaybeImmutable, - PolicyEffectScope, WindowsConditionEntry, } from '../../../../../common/endpoint/types'; @@ -27,15 +23,3 @@ export const isMacosLinuxTrustedAppCondition = ( ): condition is MacosLinuxConditionEntry => { return condition.field !== ConditionEntryField.SIGNER; }; - -export const isGlobalEffectScope = ( - effectedScope: MaybeImmutable -): effectedScope is GlobalEffectScope => { - return effectedScope.type === 'global'; -}; - -export const isPolicyEffectScope = ( - effectedScope: MaybeImmutable -): effectedScope is PolicyEffectScope => { - return effectedScope.type === 'policy'; -}; diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/action.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/action.ts deleted file mode 100644 index f9965da4a225..000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/action.ts +++ /dev/null @@ -1,95 +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 { Action } from 'redux'; - -import { NewTrustedApp, TrustedApp } from '../../../../../common/endpoint/types'; -import { AsyncResourceState, TrustedAppsListData } from '../state'; -import { GetPolicyListResponse } from '../../policy/types'; - -export type TrustedAppsListDataOutdated = Action<'trustedAppsListDataOutdated'>; - -interface ResourceStateChanged extends Action { - payload: { newState: AsyncResourceState }; -} - -export type TrustedAppsListResourceStateChanged = ResourceStateChanged< - 'trustedAppsListResourceStateChanged', - TrustedAppsListData ->; - -export type TrustedAppDeletionSubmissionResourceStateChanged = - ResourceStateChanged<'trustedAppDeletionSubmissionResourceStateChanged'>; - -export type TrustedAppDeletionDialogStarted = Action<'trustedAppDeletionDialogStarted'> & { - payload: { - entry: TrustedApp; - }; -}; - -export type TrustedAppDeletionDialogConfirmed = Action<'trustedAppDeletionDialogConfirmed'>; - -export type TrustedAppDeletionDialogClosed = Action<'trustedAppDeletionDialogClosed'>; - -export type TrustedAppCreationSubmissionResourceStateChanged = ResourceStateChanged< - 'trustedAppCreationSubmissionResourceStateChanged', - TrustedApp ->; - -export type TrustedAppCreationDialogStarted = Action<'trustedAppCreationDialogStarted'> & { - payload: { - entry: NewTrustedApp; - }; -}; - -export type TrustedAppCreationDialogFormStateUpdated = - Action<'trustedAppCreationDialogFormStateUpdated'> & { - payload: { - entry: NewTrustedApp; - isValid: boolean; - }; - }; - -export type TrustedAppCreationEditItemStateChanged = - Action<'trustedAppCreationEditItemStateChanged'> & { - payload: AsyncResourceState; - }; - -export type TrustedAppCreationDialogConfirmed = Action<'trustedAppCreationDialogConfirmed'>; - -export type TrustedAppCreationDialogClosed = Action<'trustedAppCreationDialogClosed'>; - -export type TrustedAppsExistResponse = Action<'trustedAppsExistStateChanged'> & { - payload: AsyncResourceState; -}; - -export type TrustedAppsPoliciesStateChanged = Action<'trustedAppsPoliciesStateChanged'> & { - payload: AsyncResourceState; -}; - -export type TrustedAppForceRefresh = Action<'trustedAppForceRefresh'> & { - payload: { - forceRefresh: boolean; - }; -}; - -export type TrustedAppsPageAction = - | TrustedAppsListDataOutdated - | TrustedAppsListResourceStateChanged - | TrustedAppDeletionSubmissionResourceStateChanged - | TrustedAppDeletionDialogStarted - | TrustedAppDeletionDialogConfirmed - | TrustedAppDeletionDialogClosed - | TrustedAppCreationSubmissionResourceStateChanged - | TrustedAppCreationEditItemStateChanged - | TrustedAppCreationDialogStarted - | TrustedAppCreationDialogFormStateUpdated - | TrustedAppCreationDialogConfirmed - | TrustedAppsExistResponse - | TrustedAppsPoliciesStateChanged - | TrustedAppCreationDialogClosed - | TrustedAppForceRefresh; diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/builders.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/builders.ts deleted file mode 100644 index f0fab9818892..000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/builders.ts +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { ConditionEntryField, OperatingSystem } from '@kbn/securitysolution-utils'; -import { TrustedAppConditionEntry, NewTrustedApp } from '../../../../../common/endpoint/types'; - -import { MANAGEMENT_DEFAULT_PAGE, MANAGEMENT_DEFAULT_PAGE_SIZE } from '../../../common/constants'; - -import { TrustedAppsListPageState } from '../state'; - -export const defaultConditionEntry = (): TrustedAppConditionEntry => ({ - field: ConditionEntryField.HASH, - operator: 'included', - type: 'match', - value: '', -}); - -export const defaultNewTrustedApp = (): NewTrustedApp => ({ - name: '', - os: OperatingSystem.WINDOWS, - entries: [defaultConditionEntry()], - description: '', - effectScope: { type: 'global' }, -}); - -export const initialDeletionDialogState = (): TrustedAppsListPageState['deletionDialog'] => ({ - confirmed: false, - submissionResourceState: { type: 'UninitialisedResourceState' }, -}); - -export const initialCreationDialogState = (): TrustedAppsListPageState['creationDialog'] => ({ - confirmed: false, - submissionResourceState: { type: 'UninitialisedResourceState' }, -}); - -export const initialTrustedAppsPageState = (): TrustedAppsListPageState => ({ - entriesExist: { type: 'UninitialisedResourceState' }, - listView: { - listResourceState: { type: 'UninitialisedResourceState' }, - freshDataTimestamp: Date.now(), - }, - deletionDialog: initialDeletionDialogState(), - creationDialog: initialCreationDialogState(), - policies: { type: 'UninitialisedResourceState' }, - location: { - page_index: MANAGEMENT_DEFAULT_PAGE, - page_size: MANAGEMENT_DEFAULT_PAGE_SIZE, - show: undefined, - id: undefined, - view_type: 'grid', - filter: '', - included_policies: '', - }, - active: false, - forceRefresh: false, -}); diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.test.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.test.ts deleted file mode 100644 index 4455baddb047..000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.test.ts +++ /dev/null @@ -1,459 +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 { applyMiddleware, createStore } from 'redux'; - -import { createSpyMiddleware } from '../../../../common/store/test_utils'; - -import { - createDefaultPagination, - createListLoadedResourceState, - createLoadedListViewWithPagination, - createSampleTrustedApp, - createSampleTrustedApps, - createServerApiError, - createUninitialisedResourceState, - createUserChangedUrlAction, -} from '../test_utils'; - -import { TrustedAppsService } from '../service'; -import { Pagination, TrustedAppsListPageLocation, TrustedAppsListPageState } from '../state'; -import { initialTrustedAppsPageState } from './builders'; -import { trustedAppsPageReducer } from './reducer'; -import { createTrustedAppsPageMiddleware } from './middleware'; -import { Immutable } from '../../../../../common/endpoint/types'; -import { getGeneratedPolicyResponse } from './mocks'; - -const initialNow = 111111; -const dateNowMock = jest.fn(); -dateNowMock.mockReturnValue(initialNow); - -Date.now = dateNowMock; - -const initialState: Immutable = initialTrustedAppsPageState(); - -const createGetTrustedListAppsResponse = (pagination: Partial) => { - const fullPagination = { ...createDefaultPagination(), ...pagination }; - - return { - data: createSampleTrustedApps(pagination), - page: fullPagination.pageIndex, - per_page: fullPagination.pageSize, - total: fullPagination.totalItemCount, - }; -}; - -const createTrustedAppsServiceMock = (): jest.Mocked => ({ - getTrustedAppsList: jest.fn(), - deleteTrustedApp: jest.fn(), - createTrustedApp: jest.fn(), - getPolicyList: jest.fn(), - updateTrustedApp: jest.fn(), - getTrustedApp: jest.fn(), - assignPolicyToTrustedApps: jest.fn(), - removePolicyFromTrustedApps: jest.fn(), -}); - -const createStoreSetup = (trustedAppsService: TrustedAppsService) => { - const spyMiddleware = createSpyMiddleware(); - - return { - spyMiddleware, - store: createStore( - trustedAppsPageReducer, - applyMiddleware( - createTrustedAppsPageMiddleware(trustedAppsService), - spyMiddleware.actionSpyMiddleware - ) - ), - }; -}; - -describe('middleware', () => { - type TrustedAppsEntriesExistState = Pick; - const entriesExistLoadedState = (): TrustedAppsEntriesExistState => { - return { - entriesExist: { - data: true, - type: 'LoadedResourceState', - }, - }; - }; - const entriesExistLoadingState = (): TrustedAppsEntriesExistState => { - return { - entriesExist: { - previousState: { - type: 'UninitialisedResourceState', - }, - type: 'LoadingResourceState', - }, - }; - }; - - const createLocationState = ( - params?: Partial - ): TrustedAppsListPageLocation => { - return { - ...initialState.location, - ...(params ?? {}), - }; - }; - - beforeEach(() => { - dateNowMock.mockReturnValue(initialNow); - }); - - describe('initial state', () => { - it('sets initial state properly', async () => { - expect(createStoreSetup(createTrustedAppsServiceMock()).store.getState()).toStrictEqual( - initialState - ); - }); - }); - - describe('refreshing list resource state', () => { - it('refreshes the list when location changes and data gets outdated', async () => { - const pagination = { pageIndex: 2, pageSize: 50 }; - const location = createLocationState({ - page_index: 2, - page_size: 50, - }); - const service = createTrustedAppsServiceMock(); - const { store, spyMiddleware } = createStoreSetup(service); - - service.getTrustedAppsList.mockResolvedValue(createGetTrustedListAppsResponse(pagination)); - - store.dispatch( - createUserChangedUrlAction('/administration/trusted_apps', '?page_index=2&page_size=50') - ); - - expect(store.getState()).toStrictEqual({ - ...initialState, - listView: { - listResourceState: { - type: 'LoadingResourceState', - previousState: createUninitialisedResourceState(), - }, - freshDataTimestamp: initialNow, - }, - active: true, - location, - }); - - await spyMiddleware.waitForAction('trustedAppsListResourceStateChanged'); - - expect(store.getState()).toStrictEqual({ - ...initialState, - ...entriesExistLoadingState(), - listView: createLoadedListViewWithPagination(initialNow, pagination), - active: true, - location, - }); - }); - - it('does not refresh the list when location changes and data does not get outdated', async () => { - const pagination = { pageIndex: 2, pageSize: 50 }; - const location = createLocationState({ - page_index: 2, - page_size: 50, - }); - const service = createTrustedAppsServiceMock(); - const { store, spyMiddleware } = createStoreSetup(service); - - service.getTrustedAppsList.mockResolvedValue(createGetTrustedListAppsResponse(pagination)); - - store.dispatch( - createUserChangedUrlAction('/administration/trusted_apps', '?page_index=2&page_size=50') - ); - - await spyMiddleware.waitForAction('trustedAppsListResourceStateChanged'); - - store.dispatch( - createUserChangedUrlAction('/administration/trusted_apps', '?page_index=2&page_size=50') - ); - - expect(service.getTrustedAppsList).toBeCalledTimes(2); - expect(store.getState()).toStrictEqual({ - ...initialState, - ...entriesExistLoadingState(), - listView: createLoadedListViewWithPagination(initialNow, pagination), - active: true, - location, - }); - }); - - it('refreshes the list when data gets outdated with and outdate action', async () => { - const newNow = 222222; - const pagination = { pageIndex: 0, pageSize: 10 }; - const location = createLocationState(); - const service = createTrustedAppsServiceMock(); - const { store, spyMiddleware } = createStoreSetup(service); - const policiesResponse = getGeneratedPolicyResponse(); - - service.getTrustedAppsList.mockResolvedValue(createGetTrustedListAppsResponse(pagination)); - service.getPolicyList.mockResolvedValue(policiesResponse); - - store.dispatch(createUserChangedUrlAction('/administration/trusted_apps')); - - await spyMiddleware.waitForAction('trustedAppsListResourceStateChanged'); - - dateNowMock.mockReturnValue(newNow); - - store.dispatch({ type: 'trustedAppsListDataOutdated' }); - - expect(store.getState()).toStrictEqual({ - ...initialState, - ...entriesExistLoadingState(), - listView: { - listResourceState: { - type: 'LoadingResourceState', - previousState: createListLoadedResourceState(pagination, initialNow), - }, - freshDataTimestamp: newNow, - }, - active: true, - location, - }); - - await spyMiddleware.waitForAction('trustedAppsListResourceStateChanged'); - await spyMiddleware.waitForAction('trustedAppsPoliciesStateChanged'); - - expect(store.getState()).toStrictEqual({ - ...initialState, - ...entriesExistLoadedState(), - policies: { - data: policiesResponse, - type: 'LoadedResourceState', - }, - listView: createLoadedListViewWithPagination(newNow, pagination), - active: true, - location, - }); - }); - - it('set list resource state to failed when failing to load data', async () => { - const service = createTrustedAppsServiceMock(); - const { store, spyMiddleware } = createStoreSetup(service); - - service.getTrustedAppsList.mockRejectedValue({ - body: createServerApiError('Internal Server Error'), - }); - - store.dispatch( - createUserChangedUrlAction('/administration/trusted_apps', '?page_index=2&page_size=50') - ); - - await spyMiddleware.waitForAction('trustedAppsListResourceStateChanged'); - - expect(store.getState()).toStrictEqual({ - ...initialState, - ...entriesExistLoadingState(), - listView: { - listResourceState: { - type: 'FailedResourceState', - error: createServerApiError('Internal Server Error'), - lastLoadedState: undefined, - }, - freshDataTimestamp: initialNow, - }, - active: true, - location: createLocationState({ - page_index: 2, - page_size: 50, - }), - }); - - const infiniteLoopTest = async () => { - await spyMiddleware.waitForAction('trustedAppsListResourceStateChanged'); - }; - - await expect(infiniteLoopTest).rejects.not.toBeNull(); - }); - }); - - describe('submitting deletion dialog', () => { - const newNow = 222222; - const entry = createSampleTrustedApp(3); - const notFoundError = createServerApiError('Not Found'); - const pagination = { pageIndex: 0, pageSize: 10 }; - const location = createLocationState(); - const getTrustedAppsListResponse = createGetTrustedListAppsResponse(pagination); - const listView = createLoadedListViewWithPagination(initialNow, pagination); - const listViewNew = createLoadedListViewWithPagination(newNow, pagination); - const testStartState = { - ...initialState, - ...entriesExistLoadingState(), - listView, - active: true, - location, - }; - - it('does not submit when entry is undefined', async () => { - const service = createTrustedAppsServiceMock(); - const { store, spyMiddleware } = createStoreSetup(service); - - service.getTrustedAppsList.mockResolvedValue(getTrustedAppsListResponse); - service.deleteTrustedApp.mockResolvedValue(); - - store.dispatch(createUserChangedUrlAction('/administration/trusted_apps')); - - await spyMiddleware.waitForAction('trustedAppsListResourceStateChanged'); - - store.dispatch({ type: 'trustedAppDeletionDialogConfirmed' }); - - expect(store.getState()).toStrictEqual({ - ...testStartState, - deletionDialog: { ...testStartState.deletionDialog, confirmed: true }, - }); - }); - - it('submits successfully when entry is defined', async () => { - const service = createTrustedAppsServiceMock(); - const { store, spyMiddleware } = createStoreSetup(service); - const policiesResponse = getGeneratedPolicyResponse(); - - service.getTrustedAppsList.mockResolvedValue(getTrustedAppsListResponse); - service.deleteTrustedApp.mockResolvedValue(); - service.getPolicyList.mockResolvedValue(policiesResponse); - - store.dispatch(createUserChangedUrlAction('/administration/trusted_apps')); - - await spyMiddleware.waitForAction('trustedAppsListResourceStateChanged'); - - dateNowMock.mockReturnValue(newNow); - - store.dispatch({ type: 'trustedAppDeletionDialogStarted', payload: { entry } }); - store.dispatch({ type: 'trustedAppDeletionDialogConfirmed' }); - - expect(store.getState()).toStrictEqual({ - ...testStartState, - deletionDialog: { - entry, - confirmed: true, - submissionResourceState: { - type: 'LoadingResourceState', - previousState: { type: 'UninitialisedResourceState' }, - }, - }, - }); - - await spyMiddleware.waitForAction('trustedAppDeletionSubmissionResourceStateChanged'); - await spyMiddleware.waitForAction('trustedAppsListResourceStateChanged'); - - expect(store.getState()).toStrictEqual({ - ...testStartState, - ...entriesExistLoadedState(), - policies: { - data: policiesResponse, - type: 'LoadedResourceState', - }, - listView: listViewNew, - }); - expect(service.deleteTrustedApp).toBeCalledWith({ id: '3' }); - expect(service.deleteTrustedApp).toBeCalledTimes(1); - }); - - it('does not submit twice', async () => { - const service = createTrustedAppsServiceMock(); - const { store, spyMiddleware } = createStoreSetup(service); - const policiesResponse = getGeneratedPolicyResponse(); - - service.getTrustedAppsList.mockResolvedValue(getTrustedAppsListResponse); - service.deleteTrustedApp.mockResolvedValue(); - service.getPolicyList.mockResolvedValue(policiesResponse); - - store.dispatch(createUserChangedUrlAction('/administration/trusted_apps')); - - await spyMiddleware.waitForAction('trustedAppsListResourceStateChanged'); - - dateNowMock.mockReturnValue(newNow); - - store.dispatch({ type: 'trustedAppDeletionDialogStarted', payload: { entry } }); - store.dispatch({ type: 'trustedAppDeletionDialogConfirmed' }); - store.dispatch({ type: 'trustedAppDeletionDialogConfirmed' }); - - expect(store.getState()).toStrictEqual({ - ...testStartState, - deletionDialog: { - entry, - confirmed: true, - submissionResourceState: { - type: 'LoadingResourceState', - previousState: { type: 'UninitialisedResourceState' }, - }, - }, - }); - - await spyMiddleware.waitForAction('trustedAppDeletionSubmissionResourceStateChanged'); - await spyMiddleware.waitForAction('trustedAppsListResourceStateChanged'); - - expect(store.getState()).toStrictEqual({ - ...testStartState, - ...entriesExistLoadedState(), - policies: { - data: policiesResponse, - type: 'LoadedResourceState', - }, - listView: listViewNew, - }); - expect(service.deleteTrustedApp).toBeCalledWith({ id: '3' }); - expect(service.deleteTrustedApp).toBeCalledTimes(1); - }); - - it('does not submit when server response with failure', async () => { - const service = createTrustedAppsServiceMock(); - const { store, spyMiddleware } = createStoreSetup(service); - const policiesResponse = getGeneratedPolicyResponse(); - - service.getTrustedAppsList.mockResolvedValue(getTrustedAppsListResponse); - service.deleteTrustedApp.mockRejectedValue({ body: notFoundError }); - service.getPolicyList.mockResolvedValue(policiesResponse); - - store.dispatch(createUserChangedUrlAction('/administration/trusted_apps')); - - await spyMiddleware.waitForAction('trustedAppsListResourceStateChanged'); - - store.dispatch({ type: 'trustedAppDeletionDialogStarted', payload: { entry } }); - store.dispatch({ type: 'trustedAppDeletionDialogConfirmed' }); - - expect(store.getState()).toStrictEqual({ - ...testStartState, - deletionDialog: { - entry, - confirmed: true, - submissionResourceState: { - type: 'LoadingResourceState', - previousState: { type: 'UninitialisedResourceState' }, - }, - }, - }); - - await spyMiddleware.waitForAction('trustedAppDeletionSubmissionResourceStateChanged'); - await spyMiddleware.waitForAction('trustedAppsPoliciesStateChanged'); - - expect(store.getState()).toStrictEqual({ - ...testStartState, - ...entriesExistLoadedState(), - policies: { - data: policiesResponse, - type: 'LoadedResourceState', - }, - deletionDialog: { - entry, - confirmed: true, - submissionResourceState: { - type: 'FailedResourceState', - error: notFoundError, - lastLoadedState: undefined, - }, - }, - }); - expect(service.deleteTrustedApp).toBeCalledWith({ id: '3' }); - expect(service.deleteTrustedApp).toBeCalledTimes(1); - }); - }); -}); diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.ts deleted file mode 100644 index cd39ee27353e..000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.ts +++ /dev/null @@ -1,477 +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 { i18n } from '@kbn/i18n'; -import { - Immutable, - PostTrustedAppCreateRequest, - TrustedApp, -} from '../../../../../common/endpoint/types'; -import { AppAction } from '../../../../common/store/actions'; -import { - ImmutableMiddleware, - ImmutableMiddlewareAPI, - ImmutableMiddlewareFactory, -} from '../../../../common/store'; - -import { TrustedAppsHttpService, TrustedAppsService } from '../service'; - -import { - AsyncResourceState, - getLastLoadedResourceState, - isLoadedResourceState, - isLoadingResourceState, - isStaleResourceState, - isUninitialisedResourceState, - StaleResourceState, - TrustedAppsListData, - TrustedAppsListPageState, -} from '../state'; - -import { defaultNewTrustedApp } from './builders'; - -import { - TrustedAppCreationSubmissionResourceStateChanged, - TrustedAppDeletionSubmissionResourceStateChanged, - TrustedAppsListResourceStateChanged, -} from './action'; - -import { - getListResourceState, - getDeletionDialogEntry, - getDeletionSubmissionResourceState, - getLastLoadedListResourceState, - getCurrentLocationPageIndex, - getCurrentLocationPageSize, - getCurrentLocationFilter, - needsRefreshOfListData, - getCreationSubmissionResourceState, - getCreationDialogFormEntry, - isCreationDialogLocation, - isCreationDialogFormValid, - entriesExist, - getListTotalItemsCount, - trustedAppsListPageActive, - entriesExistState, - policiesState, - isEdit, - isFetchingEditTrustedAppItem, - editItemId, - editingTrustedApp, - getListItems, - getCurrentLocationIncludedPolicies, -} from './selectors'; -import { parsePoliciesToKQL, parseQueryFilterToKQL } from '../../../common/utils'; -import { toUpdateTrustedApp } from '../../../../../common/endpoint/service/trusted_apps/to_update_trusted_app'; -import { SEARCHABLE_FIELDS } from '../constants'; - -const createTrustedAppsListResourceStateChangedAction = ( - newState: Immutable> -): Immutable => ({ - type: 'trustedAppsListResourceStateChanged', - payload: { newState }, -}); - -const refreshListIfNeeded = async ( - store: ImmutableMiddlewareAPI, - trustedAppsService: TrustedAppsService -) => { - if (needsRefreshOfListData(store.getState())) { - store.dispatch({ type: 'trustedAppForceRefresh', payload: { forceRefresh: false } }); - store.dispatch( - createTrustedAppsListResourceStateChangedAction({ - type: 'LoadingResourceState', - // need to think on how to avoid the casting - previousState: getListResourceState(store.getState()) as Immutable< - StaleResourceState - >, - }) - ); - - try { - const pageIndex = getCurrentLocationPageIndex(store.getState()); - const pageSize = getCurrentLocationPageSize(store.getState()); - const filter = getCurrentLocationFilter(store.getState()); - const includedPolicies = getCurrentLocationIncludedPolicies(store.getState()); - - const kuery = []; - - const filterKuery = parseQueryFilterToKQL(filter, SEARCHABLE_FIELDS) || undefined; - if (filterKuery) kuery.push(filterKuery); - - const policiesKuery = - parsePoliciesToKQL(includedPolicies ? includedPolicies.split(',') : []) || undefined; - if (policiesKuery) kuery.push(policiesKuery); - - const response = await trustedAppsService.getTrustedAppsList({ - page: pageIndex + 1, - per_page: pageSize, - kuery: kuery.join(' AND ') || undefined, - }); - - store.dispatch( - createTrustedAppsListResourceStateChangedAction({ - type: 'LoadedResourceState', - data: { - items: response.data, - pageIndex, - pageSize, - totalItemsCount: response.total, - timestamp: Date.now(), - filter, - includedPolicies, - }, - }) - ); - } catch (error) { - store.dispatch( - createTrustedAppsListResourceStateChangedAction({ - type: 'FailedResourceState', - error: error.body, - lastLoadedState: getLastLoadedListResourceState(store.getState()), - }) - ); - } - } -}; - -const updateCreationDialogIfNeeded = ( - store: ImmutableMiddlewareAPI -) => { - const newEntry = getCreationDialogFormEntry(store.getState()); - const shouldShow = isCreationDialogLocation(store.getState()); - - if (shouldShow && !newEntry) { - store.dispatch({ - type: 'trustedAppCreationDialogStarted', - payload: { entry: defaultNewTrustedApp() }, - }); - } else if (!shouldShow && newEntry) { - store.dispatch({ - type: 'trustedAppCreationDialogClosed', - }); - } -}; - -const createTrustedAppCreationSubmissionResourceStateChanged = ( - newState: Immutable> -): Immutable => ({ - type: 'trustedAppCreationSubmissionResourceStateChanged', - payload: { newState }, -}); - -const submitCreationIfNeeded = async ( - store: ImmutableMiddlewareAPI, - trustedAppsService: TrustedAppsService -) => { - const currentState = store.getState(); - const submissionResourceState = getCreationSubmissionResourceState(currentState); - const isValid = isCreationDialogFormValid(currentState); - const entry = getCreationDialogFormEntry(currentState); - const editMode = isEdit(currentState); - - if (isStaleResourceState(submissionResourceState) && entry !== undefined && isValid) { - store.dispatch( - createTrustedAppCreationSubmissionResourceStateChanged({ - type: 'LoadingResourceState', - previousState: submissionResourceState, - }) - ); - - try { - let responseTrustedApp: TrustedApp; - - if (editMode) { - responseTrustedApp = ( - await trustedAppsService.updateTrustedApp( - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - { id: editItemId(currentState)! }, - // TODO: try to remove the cast - entry as PostTrustedAppCreateRequest - ) - ).data; - } else { - // TODO: try to remove the cast - responseTrustedApp = ( - await trustedAppsService.createTrustedApp(entry as PostTrustedAppCreateRequest) - ).data; - } - - store.dispatch( - createTrustedAppCreationSubmissionResourceStateChanged({ - type: 'LoadedResourceState', - data: responseTrustedApp, - }) - ); - store.dispatch({ - type: 'trustedAppsListDataOutdated', - }); - } catch (error) { - store.dispatch( - createTrustedAppCreationSubmissionResourceStateChanged({ - type: 'FailedResourceState', - error: error.body, - lastLoadedState: getLastLoadedResourceState(submissionResourceState), - }) - ); - } - } -}; - -const createTrustedAppDeletionSubmissionResourceStateChanged = ( - newState: Immutable -): Immutable => ({ - type: 'trustedAppDeletionSubmissionResourceStateChanged', - payload: { newState }, -}); - -const submitDeletionIfNeeded = async ( - store: ImmutableMiddlewareAPI, - trustedAppsService: TrustedAppsService -) => { - const submissionResourceState = getDeletionSubmissionResourceState(store.getState()); - const entry = getDeletionDialogEntry(store.getState()); - - if (isStaleResourceState(submissionResourceState) && entry !== undefined) { - store.dispatch( - createTrustedAppDeletionSubmissionResourceStateChanged({ - type: 'LoadingResourceState', - previousState: submissionResourceState, - }) - ); - - try { - await trustedAppsService.deleteTrustedApp({ id: entry.id }); - - store.dispatch( - createTrustedAppDeletionSubmissionResourceStateChanged({ - type: 'LoadedResourceState', - data: null, - }) - ); - store.dispatch({ - type: 'trustedAppDeletionDialogClosed', - }); - store.dispatch({ - type: 'trustedAppsListDataOutdated', - }); - } catch (error) { - store.dispatch( - createTrustedAppDeletionSubmissionResourceStateChanged({ - type: 'FailedResourceState', - error: error.body, - lastLoadedState: getLastLoadedResourceState(submissionResourceState), - }) - ); - } - } -}; - -const checkTrustedAppsExistIfNeeded = async ( - store: ImmutableMiddlewareAPI, - trustedAppsService: TrustedAppsService -) => { - const currentState = store.getState(); - const currentEntriesExistState = entriesExistState(currentState); - - if ( - trustedAppsListPageActive(currentState) && - !isLoadingResourceState(currentEntriesExistState) - ) { - const currentListTotal = getListTotalItemsCount(currentState); - const currentDoEntriesExist = entriesExist(currentState); - - if ( - !isLoadedResourceState(currentEntriesExistState) || - (currentListTotal === 0 && currentDoEntriesExist) || - (currentListTotal > 0 && !currentDoEntriesExist) - ) { - store.dispatch({ - type: 'trustedAppsExistStateChanged', - payload: { type: 'LoadingResourceState', previousState: currentEntriesExistState }, - }); - - let doTheyExist: boolean; - try { - const { total } = await trustedAppsService.getTrustedAppsList({ - page: 1, - per_page: 1, - }); - doTheyExist = total > 0; - } catch (e) { - // If a failure occurs, lets assume entries exits so that the UI is not blocked to the user - doTheyExist = true; - } - - store.dispatch({ - type: 'trustedAppsExistStateChanged', - payload: { type: 'LoadedResourceState', data: doTheyExist }, - }); - } - } -}; - -export const retrieveListOfPoliciesIfNeeded = async ( - { getState, dispatch }: ImmutableMiddlewareAPI, - trustedAppsService: TrustedAppsService -) => { - const currentState = getState(); - const currentPoliciesState = policiesState(currentState); - const isLoading = isLoadingResourceState(currentPoliciesState); - const isPageActive = trustedAppsListPageActive(currentState); - const isCreateFlow = isCreationDialogLocation(currentState); - const isUninitialized = isUninitialisedResourceState(currentPoliciesState); - - if (isPageActive && ((isCreateFlow && !isLoading) || isUninitialized)) { - dispatch({ - type: 'trustedAppsPoliciesStateChanged', - payload: { - type: 'LoadingResourceState', - previousState: currentPoliciesState, - } as TrustedAppsListPageState['policies'], - }); - - try { - const policyList = await trustedAppsService.getPolicyList({ - query: { - page: 1, - perPage: 1000, - }, - }); - - dispatch({ - type: 'trustedAppsPoliciesStateChanged', - payload: { - type: 'LoadedResourceState', - data: policyList, - }, - }); - } catch (error) { - dispatch({ - type: 'trustedAppsPoliciesStateChanged', - payload: { - type: 'FailedResourceState', - error: error.body || error, - lastLoadedState: getLastLoadedResourceState(policiesState(getState())), - }, - }); - } - } -}; - -const fetchEditTrustedAppIfNeeded = async ( - { getState, dispatch }: ImmutableMiddlewareAPI, - trustedAppsService: TrustedAppsService -) => { - const currentState = getState(); - const isPageActive = trustedAppsListPageActive(currentState); - const isEditFlow = isEdit(currentState); - const isAlreadyFetching = isFetchingEditTrustedAppItem(currentState); - const editTrustedAppId = editItemId(currentState); - - if (isPageActive && isEditFlow && !isAlreadyFetching) { - if (!editTrustedAppId) { - const errorMessage = i18n.translate( - 'xpack.securitySolution.trustedapps.middleware.editIdMissing', - { - defaultMessage: 'No id provided', - } - ); - - dispatch({ - type: 'trustedAppCreationEditItemStateChanged', - payload: { - type: 'FailedResourceState', - error: Object.assign(new Error(errorMessage), { statusCode: 404, error: errorMessage }), - }, - }); - return; - } - - let trustedAppForEdit = editingTrustedApp(currentState); - - // If Trusted App is already loaded, then do nothing - if (trustedAppForEdit && trustedAppForEdit.id === editTrustedAppId) { - return; - } - - // See if we can get the Trusted App record from the current list of Trusted Apps being displayed - trustedAppForEdit = getListItems(currentState).find((ta) => ta.id === editTrustedAppId); - - try { - // Retrieve Trusted App record via API if it was not in the list data. - // This would be the case when linking from another place or using an UUID for a Trusted App - // that is not currently displayed on the list view. - if (!trustedAppForEdit) { - dispatch({ - type: 'trustedAppCreationEditItemStateChanged', - payload: { - type: 'LoadingResourceState', - }, - }); - - trustedAppForEdit = (await trustedAppsService.getTrustedApp({ id: editTrustedAppId })).data; - } - - dispatch({ - type: 'trustedAppCreationEditItemStateChanged', - payload: { - type: 'LoadedResourceState', - data: trustedAppForEdit, - }, - }); - - dispatch({ - type: 'trustedAppCreationDialogFormStateUpdated', - payload: { - entry: toUpdateTrustedApp(trustedAppForEdit), - isValid: true, - }, - }); - } catch (e) { - dispatch({ - type: 'trustedAppCreationEditItemStateChanged', - payload: { - type: 'FailedResourceState', - error: e, - }, - }); - } - } -}; - -export const createTrustedAppsPageMiddleware = ( - trustedAppsService: TrustedAppsService -): ImmutableMiddleware => { - return (store) => (next) => async (action) => { - next(action); - - // TODO: need to think if failed state is a good condition to consider need for refresh - if (action.type === 'userChangedUrl' || action.type === 'trustedAppsListDataOutdated') { - await refreshListIfNeeded(store, trustedAppsService); - await checkTrustedAppsExistIfNeeded(store, trustedAppsService); - } - - if (action.type === 'userChangedUrl') { - updateCreationDialogIfNeeded(store); - retrieveListOfPoliciesIfNeeded(store, trustedAppsService); - fetchEditTrustedAppIfNeeded(store, trustedAppsService); - } - - if (action.type === 'trustedAppCreationDialogConfirmed') { - await submitCreationIfNeeded(store, trustedAppsService); - } - - if (action.type === 'trustedAppDeletionDialogConfirmed') { - await submitDeletionIfNeeded(store, trustedAppsService); - } - }; -}; - -export const trustedAppsPageMiddlewareFactory: ImmutableMiddlewareFactory< - TrustedAppsListPageState -> = (coreStart) => createTrustedAppsPageMiddleware(new TrustedAppsHttpService(coreStart.http)); diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/mocks.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/mocks.ts deleted file mode 100644 index c97dd37db6bb..000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/mocks.ts +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { GetPolicyListResponse } from '../../policy/types'; - -import { EndpointDocGenerator } from '../../../../../common/endpoint/generate_data'; - -export const getGeneratedPolicyResponse = (): GetPolicyListResponse => ({ - items: [new EndpointDocGenerator('seed').generatePolicyPackagePolicy()], - total: 1, - perPage: 1, - page: 1, -}); diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/reducer.test.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/reducer.test.ts deleted file mode 100644 index 5047608cbb4e..000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/reducer.test.ts +++ /dev/null @@ -1,235 +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 { AsyncResourceState } from '../state'; -import { initialTrustedAppsPageState } from './builders'; -import { trustedAppsPageReducer } from './reducer'; -import { - createSampleTrustedApp, - createListLoadedResourceState, - createLoadedListViewWithPagination, - createUserChangedUrlAction, - createTrustedAppsListResourceStateChangedAction, -} from '../test_utils'; - -const initialNow = 111111; -const dateNowMock = jest.fn(); -dateNowMock.mockReturnValue(initialNow); - -Date.now = dateNowMock; - -const initialState = initialTrustedAppsPageState(); - -describe('reducer', () => { - describe('UserChangedUrl', () => { - it('makes page state active and extracts all parameters', () => { - const result = trustedAppsPageReducer( - initialState, - createUserChangedUrlAction( - '/administration/trusted_apps', - '?page_index=5&page_size=50&show=create&view_type=list&filter=test&included_policies=global' - ) - ); - - expect(result).toStrictEqual({ - ...initialState, - location: { - page_index: 5, - page_size: 50, - show: 'create', - view_type: 'list', - id: undefined, - filter: 'test', - included_policies: 'global', - }, - active: true, - }); - }); - - it('extracts default pagination parameters when invalid provided', () => { - const result = trustedAppsPageReducer( - { - ...initialState, - location: { - page_index: 5, - page_size: 50, - view_type: 'grid', - filter: '', - included_policies: '', - }, - }, - createUserChangedUrlAction( - '/administration/trusted_apps', - '?page_index=b&page_size=60&show=a&view_type=c' - ) - ); - - expect(result).toStrictEqual({ ...initialState, active: true }); - }); - - it('extracts default pagination parameters when none provided', () => { - const result = trustedAppsPageReducer( - { - ...initialState, - location: { - page_index: 5, - page_size: 50, - view_type: 'grid', - filter: '', - included_policies: '', - }, - }, - createUserChangedUrlAction('/administration/trusted_apps') - ); - - expect(result).toStrictEqual({ ...initialState, active: true }); - }); - - it('makes page state inactive and resets list to uninitialised state when navigating away', () => { - const result = trustedAppsPageReducer( - { ...initialState, listView: createLoadedListViewWithPagination(initialNow), active: true }, - createUserChangedUrlAction('/administration/endpoints') - ); - - expect(result).toStrictEqual(initialState); - }); - }); - - describe('TrustedAppsListResourceStateChanged', () => { - it('sets the current list resource state', () => { - const listResourceState = createListLoadedResourceState( - { pageIndex: 3, pageSize: 50 }, - initialNow - ); - const result = trustedAppsPageReducer( - initialState, - createTrustedAppsListResourceStateChangedAction(listResourceState) - ); - - expect(result).toStrictEqual({ - ...initialState, - listView: { ...initialState.listView, listResourceState }, - }); - }); - }); - - describe('TrustedAppsListDataOutdated', () => { - it('sets the list view freshness timestamp', () => { - const newNow = 222222; - dateNowMock.mockReturnValue(newNow); - - const result = trustedAppsPageReducer(initialState, { type: 'trustedAppsListDataOutdated' }); - - expect(result).toStrictEqual({ - ...initialState, - listView: { ...initialState.listView, freshDataTimestamp: newNow }, - }); - }); - }); - - describe('TrustedAppDeletionSubmissionResourceStateChanged', () => { - it('sets the deletion dialog submission resource state', () => { - const submissionResourceState: AsyncResourceState = { - type: 'LoadedResourceState', - data: null, - }; - const result = trustedAppsPageReducer(initialState, { - type: 'trustedAppDeletionSubmissionResourceStateChanged', - payload: { newState: submissionResourceState }, - }); - - expect(result).toStrictEqual({ - ...initialState, - deletionDialog: { ...initialState.deletionDialog, submissionResourceState }, - }); - }); - }); - - describe('TrustedAppDeletionDialogStarted', () => { - it('sets the deletion dialog state to started', () => { - const entry = createSampleTrustedApp(3); - const result = trustedAppsPageReducer(initialState, { - type: 'trustedAppDeletionDialogStarted', - payload: { entry }, - }); - - expect(result).toStrictEqual({ - ...initialState, - deletionDialog: { ...initialState.deletionDialog, entry }, - }); - }); - }); - - describe('TrustedAppDeletionDialogConfirmed', () => { - it('sets the deletion dialog state to confirmed', () => { - const entry = createSampleTrustedApp(3); - const result = trustedAppsPageReducer( - { - ...initialState, - deletionDialog: { - entry, - confirmed: false, - submissionResourceState: { type: 'UninitialisedResourceState' }, - }, - }, - { type: 'trustedAppDeletionDialogConfirmed' } - ); - - expect(result).toStrictEqual({ - ...initialState, - deletionDialog: { - entry, - confirmed: true, - submissionResourceState: { type: 'UninitialisedResourceState' }, - }, - }); - }); - }); - - describe('TrustedAppDeletionDialogClosed', () => { - it('sets the deletion dialog state to confirmed', () => { - const result = trustedAppsPageReducer( - { - ...initialState, - deletionDialog: { - entry: createSampleTrustedApp(3), - confirmed: true, - submissionResourceState: { type: 'UninitialisedResourceState' }, - }, - }, - { type: 'trustedAppDeletionDialogClosed' } - ); - - expect(result).toStrictEqual(initialState); - }); - }); - - describe('TrustedAppsForceRefresh', () => { - it('sets the force refresh state to true', () => { - const result = trustedAppsPageReducer( - { - ...initialState, - forceRefresh: false, - }, - { type: 'trustedAppForceRefresh', payload: { forceRefresh: true } } - ); - - expect(result).toStrictEqual({ ...initialState, forceRefresh: true }); - }); - it('sets the force refresh state to false', () => { - const result = trustedAppsPageReducer( - { - ...initialState, - forceRefresh: true, - }, - { type: 'trustedAppForceRefresh', payload: { forceRefresh: false } } - ); - - expect(result).toStrictEqual({ ...initialState, forceRefresh: false }); - }); - }); -}); diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/reducer.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/reducer.ts deleted file mode 100644 index 07409a46156d..000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/reducer.ts +++ /dev/null @@ -1,240 +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. - */ - -// eslint-disable-next-line import/no-nodejs-modules -import { parse } from 'querystring'; -import { matchPath } from 'react-router-dom'; -import { ImmutableReducer } from '../../../../common/store'; -import { AppLocation, Immutable } from '../../../../../common/endpoint/types'; -import { UserChangedUrl } from '../../../../common/store/routing/action'; -import { AppAction } from '../../../../common/store/actions'; -import { extractTrustedAppsListPageLocation } from '../../../common/routing'; - -import { MANAGEMENT_ROUTING_TRUSTED_APPS_PATH } from '../../../common/constants'; - -import { - TrustedAppDeletionDialogClosed, - TrustedAppDeletionDialogConfirmed, - TrustedAppDeletionDialogStarted, - TrustedAppDeletionSubmissionResourceStateChanged, - TrustedAppCreationSubmissionResourceStateChanged, - TrustedAppsListDataOutdated, - TrustedAppsListResourceStateChanged, - TrustedAppCreationDialogStarted, - TrustedAppCreationDialogFormStateUpdated, - TrustedAppCreationDialogConfirmed, - TrustedAppCreationDialogClosed, - TrustedAppsExistResponse, - TrustedAppsPoliciesStateChanged, - TrustedAppCreationEditItemStateChanged, - TrustedAppForceRefresh, -} from './action'; - -import { TrustedAppsListPageState } from '../state'; -import { - initialCreationDialogState, - initialDeletionDialogState, - initialTrustedAppsPageState, -} from './builders'; -import { entriesExistState, trustedAppsListPageActive } from './selectors'; - -type StateReducer = ImmutableReducer; -type CaseReducer = ( - state: Immutable, - action: Immutable -) => Immutable; - -const isTrustedAppsPageLocation = (location: Immutable) => { - return ( - matchPath(location.pathname ?? '', { - path: MANAGEMENT_ROUTING_TRUSTED_APPS_PATH, - exact: true, - }) !== null - ); -}; - -const trustedAppsListDataOutdated: CaseReducer = (state, action) => { - return { ...state, listView: { ...state.listView, freshDataTimestamp: Date.now() } }; -}; - -const trustedAppsListResourceStateChanged: CaseReducer = ( - state, - action -) => { - return { ...state, listView: { ...state.listView, listResourceState: action.payload.newState } }; -}; - -const trustedAppDeletionSubmissionResourceStateChanged: CaseReducer< - TrustedAppDeletionSubmissionResourceStateChanged -> = (state, action) => { - return { - ...state, - deletionDialog: { ...state.deletionDialog, submissionResourceState: action.payload.newState }, - }; -}; - -const trustedAppDeletionDialogStarted: CaseReducer = ( - state, - action -) => { - return { ...state, deletionDialog: { ...initialDeletionDialogState(), ...action.payload } }; -}; - -const trustedAppDeletionDialogConfirmed: CaseReducer = ( - state -) => { - return { ...state, deletionDialog: { ...state.deletionDialog, confirmed: true } }; -}; - -const trustedAppDeletionDialogClosed: CaseReducer = (state) => { - return { ...state, deletionDialog: initialDeletionDialogState() }; -}; - -const trustedAppCreationSubmissionResourceStateChanged: CaseReducer< - TrustedAppCreationSubmissionResourceStateChanged -> = (state, action) => { - return { - ...state, - creationDialog: { ...state.creationDialog, submissionResourceState: action.payload.newState }, - }; -}; - -const trustedAppCreationDialogStarted: CaseReducer = ( - state, - action -) => { - return { - ...state, - creationDialog: { - ...initialCreationDialogState(), - formState: { ...action.payload, isValid: false }, - }, - }; -}; - -const trustedAppCreationDialogFormStateUpdated: CaseReducer< - TrustedAppCreationDialogFormStateUpdated -> = (state, action) => { - return { - ...state, - creationDialog: { ...state.creationDialog, formState: { ...action.payload } }, - }; -}; - -const handleUpdateToEditItemState: CaseReducer = ( - state, - action -) => { - return { - ...state, - creationDialog: { ...state.creationDialog, editItem: action.payload }, - }; -}; - -const trustedAppCreationDialogConfirmed: CaseReducer = ( - state -) => { - return { ...state, creationDialog: { ...state.creationDialog, confirmed: true } }; -}; - -const trustedAppCreationDialogClosed: CaseReducer = (state) => { - return { ...state, creationDialog: initialCreationDialogState() }; -}; - -const userChangedUrl: CaseReducer = (state, action) => { - if (isTrustedAppsPageLocation(action.payload)) { - const location = extractTrustedAppsListPageLocation(parse(action.payload.search.slice(1))); - - return { ...state, active: true, location }; - } else { - return initialTrustedAppsPageState(); - } -}; - -const updateEntriesExists: CaseReducer = (state, { payload }) => { - if (entriesExistState(state) !== payload) { - return { - ...state, - entriesExist: payload, - }; - } - return state; -}; - -const updatePolicies: CaseReducer = (state, { payload }) => { - if (trustedAppsListPageActive(state)) { - return { - ...state, - policies: payload, - }; - } - return state; -}; - -const forceRefresh: CaseReducer = (state, { payload }) => { - return { - ...state, - forceRefresh: payload.forceRefresh, - }; -}; - -export const trustedAppsPageReducer: StateReducer = ( - state = initialTrustedAppsPageState(), - action -) => { - switch (action.type) { - case 'trustedAppsListDataOutdated': - return trustedAppsListDataOutdated(state, action); - - case 'trustedAppsListResourceStateChanged': - return trustedAppsListResourceStateChanged(state, action); - - case 'trustedAppDeletionSubmissionResourceStateChanged': - return trustedAppDeletionSubmissionResourceStateChanged(state, action); - - case 'trustedAppDeletionDialogStarted': - return trustedAppDeletionDialogStarted(state, action); - - case 'trustedAppDeletionDialogConfirmed': - return trustedAppDeletionDialogConfirmed(state, action); - - case 'trustedAppDeletionDialogClosed': - return trustedAppDeletionDialogClosed(state, action); - - case 'trustedAppCreationSubmissionResourceStateChanged': - return trustedAppCreationSubmissionResourceStateChanged(state, action); - - case 'trustedAppCreationDialogStarted': - return trustedAppCreationDialogStarted(state, action); - - case 'trustedAppCreationDialogFormStateUpdated': - return trustedAppCreationDialogFormStateUpdated(state, action); - - case 'trustedAppCreationEditItemStateChanged': - return handleUpdateToEditItemState(state, action); - - case 'trustedAppCreationDialogConfirmed': - return trustedAppCreationDialogConfirmed(state, action); - - case 'trustedAppCreationDialogClosed': - return trustedAppCreationDialogClosed(state, action); - - case 'userChangedUrl': - return userChangedUrl(state, action); - - case 'trustedAppsExistStateChanged': - return updateEntriesExists(state, action); - - case 'trustedAppsPoliciesStateChanged': - return updatePolicies(state, action); - - case 'trustedAppForceRefresh': - return forceRefresh(state, action); - } - - return state; -}; diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.test.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.test.ts deleted file mode 100644 index 4468d044827c..000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.test.ts +++ /dev/null @@ -1,388 +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 { - AsyncResourceState, - TrustedAppsListPageLocation, - TrustedAppsListPageState, -} from '../state'; -import { initialTrustedAppsPageState } from './builders'; -import { - getListResourceState, - getLastLoadedListResourceState, - getCurrentLocationPageIndex, - getCurrentLocationPageSize, - getListErrorMessage, - getListItems, - getListTotalItemsCount, - isListLoading, - needsRefreshOfListData, - isDeletionDialogOpen, - isDeletionInProgress, - isDeletionSuccessful, - getDeletionError, - getDeletionDialogEntry, - getDeletionSubmissionResourceState, -} from './selectors'; - -import { - createDefaultPagination, - createListComplexLoadingResourceState, - createListFailedResourceState, - createListLoadedResourceState, - createLoadedListViewWithPagination, - createSampleTrustedApp, - createSampleTrustedApps, - createServerApiError, - createUninitialisedResourceState, -} from '../test_utils'; - -const initialNow = 111111; -const dateNowMock = jest.fn(); -dateNowMock.mockReturnValue(initialNow); - -Date.now = dateNowMock; - -const initialState = initialTrustedAppsPageState(); - -const createStateWithDeletionSubmissionResourceState = ( - submissionResourceState: AsyncResourceState -): TrustedAppsListPageState => ({ - ...initialState, - deletionDialog: { ...initialState.deletionDialog, submissionResourceState }, -}); - -describe('selectors', () => { - describe('needsRefreshOfListData()', () => { - it('returns false for outdated resource state and inactive state', () => { - expect(needsRefreshOfListData(initialState)).toBe(false); - }); - - it('returns true for outdated resource state and active state', () => { - expect(needsRefreshOfListData({ ...initialState, active: true })).toBe(true); - }); - - it('returns true when current loaded page index is outdated', () => { - const listView = createLoadedListViewWithPagination(initialNow, { pageIndex: 1 }); - - expect(needsRefreshOfListData({ ...initialState, listView, active: true })).toBe(true); - }); - - it('returns true when current loaded page size is outdated', () => { - const listView = createLoadedListViewWithPagination(initialNow, { pageSize: 50 }); - - expect(needsRefreshOfListData({ ...initialState, listView, active: true })).toBe(true); - }); - - it('returns true when current loaded data timestamp is outdated', () => { - const listView = { - ...createLoadedListViewWithPagination(111111), - freshDataTimestamp: 222222, - }; - - expect(needsRefreshOfListData({ ...initialState, listView, active: true })).toBe(true); - }); - - it('returns false when current loaded data is up to date', () => { - const listView = createLoadedListViewWithPagination(initialNow); - const location: TrustedAppsListPageLocation = { - page_index: 0, - page_size: 10, - view_type: 'grid', - filter: '', - included_policies: '', - }; - - expect(needsRefreshOfListData({ ...initialState, listView, active: true, location })).toBe( - false - ); - }); - }); - - describe('getListResourceState()', () => { - it('returns current list resource state', () => { - expect(getListResourceState(initialState)).toStrictEqual(createUninitialisedResourceState()); - }); - }); - - describe('getLastLoadedListResourceState()', () => { - it('returns last loaded list resource state', () => { - const state = { - ...initialState, - listView: { - listResourceState: createListComplexLoadingResourceState( - createDefaultPagination(), - initialNow - ), - freshDataTimestamp: initialNow, - }, - }; - - expect(getLastLoadedListResourceState(state)).toStrictEqual( - createListLoadedResourceState(createDefaultPagination(), initialNow) - ); - }); - }); - - describe('getListItems()', () => { - it('returns empty list when no valid data loaded', () => { - expect(getListItems(initialState)).toStrictEqual([]); - }); - - it('returns last loaded list items', () => { - const state = { - ...initialState, - listView: { - listResourceState: createListComplexLoadingResourceState( - createDefaultPagination(), - initialNow - ), - freshDataTimestamp: initialNow, - }, - }; - - expect(getListItems(state)).toStrictEqual(createSampleTrustedApps(createDefaultPagination())); - }); - }); - - describe('getListTotalItemsCount()', () => { - it('returns 0 when no valid data loaded', () => { - expect(getListTotalItemsCount(initialState)).toBe(0); - }); - - it('returns last loaded total items count', () => { - const state = { - ...initialState, - listView: { - listResourceState: createListComplexLoadingResourceState( - createDefaultPagination(), - initialNow - ), - freshDataTimestamp: initialNow, - }, - }; - - expect(getListTotalItemsCount(state)).toBe(200); - }); - }); - - describe('getListCurrentPageIndex()', () => { - it('returns page index', () => { - const location: TrustedAppsListPageLocation = { - page_index: 3, - page_size: 10, - view_type: 'grid', - filter: '', - included_policies: '', - }; - - expect(getCurrentLocationPageIndex({ ...initialState, location })).toBe(3); - }); - }); - - describe('getListCurrentPageSize()', () => { - it('returns page size', () => { - const location: TrustedAppsListPageLocation = { - page_index: 0, - page_size: 20, - view_type: 'grid', - filter: '', - included_policies: '', - }; - - expect(getCurrentLocationPageSize({ ...initialState, location })).toBe(20); - }); - }); - - describe('getListErrorMessage()', () => { - it('returns undefined when not in failed state', () => { - const state = { - ...initialState, - listView: { - listResourceState: createListComplexLoadingResourceState( - createDefaultPagination(), - initialNow - ), - freshDataTimestamp: initialNow, - }, - }; - - expect(getListErrorMessage(state)).toBeUndefined(); - }); - - it('returns message when not in failed state', () => { - const state = { - ...initialState, - listView: { - listResourceState: createListFailedResourceState('Internal Server Error'), - freshDataTimestamp: initialNow, - }, - }; - - expect(getListErrorMessage(state)).toBe('Internal Server Error'); - }); - }); - - describe('isListLoading()', () => { - it('returns false when no loading is happening', () => { - expect(isListLoading(initialState)).toBe(false); - }); - - it('returns true when loading is in progress', () => { - const state = { - ...initialState, - listView: { - listResourceState: createListComplexLoadingResourceState( - createDefaultPagination(), - initialNow - ), - freshDataTimestamp: initialNow, - }, - }; - - expect(isListLoading(state)).toBe(true); - }); - }); - - describe('isDeletionDialogOpen()', () => { - it('returns false when no entry is set', () => { - expect(isDeletionDialogOpen(initialState)).toBe(false); - }); - - it('returns true when entry is set', () => { - const state = { - ...initialState, - deletionDialog: { - ...initialState.deletionDialog, - entry: createSampleTrustedApp(5), - }, - }; - - expect(isDeletionDialogOpen(state)).toBe(true); - }); - }); - - describe('isDeletionInProgress()', () => { - it('returns false when resource state is uninitialised', () => { - expect(isDeletionInProgress(initialState)).toBe(false); - }); - - it('returns true when resource state is loading', () => { - const state = createStateWithDeletionSubmissionResourceState({ - type: 'LoadingResourceState', - previousState: { type: 'UninitialisedResourceState' }, - }); - - expect(isDeletionInProgress(state)).toBe(true); - }); - - it('returns false when resource state is loaded', () => { - const state = createStateWithDeletionSubmissionResourceState({ - type: 'LoadedResourceState', - data: null, - }); - - expect(isDeletionInProgress(state)).toBe(false); - }); - - it('returns false when resource state is failed', () => { - const state = createStateWithDeletionSubmissionResourceState({ - type: 'FailedResourceState', - error: createServerApiError('Not Found'), - }); - - expect(isDeletionInProgress(state)).toBe(false); - }); - }); - - describe('isDeletionSuccessful()', () => { - it('returns false when resource state is uninitialised', () => { - expect(isDeletionSuccessful(initialState)).toBe(false); - }); - - it('returns false when resource state is loading', () => { - const state = createStateWithDeletionSubmissionResourceState({ - type: 'LoadingResourceState', - previousState: { type: 'UninitialisedResourceState' }, - }); - - expect(isDeletionSuccessful(state)).toBe(false); - }); - - it('returns true when resource state is loaded', () => { - const state = createStateWithDeletionSubmissionResourceState({ - type: 'LoadedResourceState', - data: null, - }); - - expect(isDeletionSuccessful(state)).toBe(true); - }); - - it('returns false when resource state is failed', () => { - const state = createStateWithDeletionSubmissionResourceState({ - type: 'FailedResourceState', - error: createServerApiError('Not Found'), - }); - - expect(isDeletionSuccessful(state)).toBe(false); - }); - }); - - describe('getDeletionError()', () => { - it('returns undefined when resource state is uninitialised', () => { - expect(getDeletionError(initialState)).toBeUndefined(); - }); - - it('returns undefined when resource state is loading', () => { - const state = createStateWithDeletionSubmissionResourceState({ - type: 'LoadingResourceState', - previousState: { type: 'UninitialisedResourceState' }, - }); - - expect(getDeletionError(state)).toBeUndefined(); - }); - - it('returns undefined when resource state is loaded', () => { - const state = createStateWithDeletionSubmissionResourceState({ - type: 'LoadedResourceState', - data: null, - }); - - expect(getDeletionError(state)).toBeUndefined(); - }); - - it('returns error when resource state is failed', () => { - const state = createStateWithDeletionSubmissionResourceState({ - type: 'FailedResourceState', - error: createServerApiError('Not Found'), - }); - - expect(getDeletionError(state)).toStrictEqual(createServerApiError('Not Found')); - }); - }); - - describe('getDeletionSubmissionResourceState()', () => { - it('returns submission resource state', () => { - expect(getDeletionSubmissionResourceState(initialState)).toStrictEqual({ - type: 'UninitialisedResourceState', - }); - }); - }); - - describe('getDeletionDialogEntry()', () => { - it('returns undefined when no entry is set', () => { - expect(getDeletionDialogEntry(initialState)).toBeUndefined(); - }); - - it('returns entry when entry is set', () => { - const entry = createSampleTrustedApp(5); - const state = { ...initialState, deletionDialog: { ...initialState.deletionDialog, entry } }; - - expect(getDeletionDialogEntry(state)).toStrictEqual(entry); - }); - }); -}); diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.ts deleted file mode 100644 index 743d8b84760b..000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.ts +++ /dev/null @@ -1,277 +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 { createSelector } from 'reselect'; -import { ServerApiError } from '../../../../common/types'; -import { - Immutable, - NewTrustedApp, - PolicyData, - TrustedApp, -} from '../../../../../common/endpoint/types'; -import { MANAGEMENT_PAGE_SIZE_OPTIONS } from '../../../common/constants'; - -import { - AsyncResourceState, - getCurrentResourceError, - getLastLoadedResourceState, - isFailedResourceState, - isLoadedResourceState, - isLoadingResourceState, - isOutdatedResourceState, - LoadedResourceState, - Pagination, - TrustedAppsListData, - TrustedAppsListPageLocation, - TrustedAppsListPageState, -} from '../state'; -import { GetPolicyListResponse } from '../../policy/types'; - -export const needsRefreshOfListData = (state: Immutable): boolean => { - const freshDataTimestamp = state.listView.freshDataTimestamp; - const currentPage = state.listView.listResourceState; - const location = state.location; - const forceRefresh = state.forceRefresh; - return ( - Boolean(state.active) && - (forceRefresh || - isOutdatedResourceState(currentPage, (data) => { - return ( - data.pageIndex === location.page_index && - data.pageSize === location.page_size && - data.timestamp >= freshDataTimestamp - ); - })) - ); -}; - -export const getListResourceState = ( - state: Immutable -): Immutable> | undefined => { - return state.listView.listResourceState; -}; - -export const getLastLoadedListResourceState = ( - state: Immutable -): Immutable> | undefined => { - return getLastLoadedResourceState(state.listView.listResourceState); -}; - -export const getListItems = ( - state: Immutable -): Immutable => { - return getLastLoadedResourceState(state.listView.listResourceState)?.data.items || []; -}; - -export const getCurrentLocationPageIndex = (state: Immutable): number => { - return state.location.page_index; -}; - -export const getCurrentLocationPageSize = (state: Immutable): number => { - return state.location.page_size; -}; - -export const getCurrentLocationFilter = (state: Immutable): string => { - return state.location.filter; -}; - -export const getCurrentLocationIncludedPolicies = ( - state: Immutable -): string => { - return state.location.included_policies; -}; - -export const getListTotalItemsCount = (state: Immutable): number => { - return getLastLoadedResourceState(state.listView.listResourceState)?.data.totalItemsCount || 0; -}; - -export const getListPagination = (state: Immutable): Pagination => { - const lastLoadedResourceState = getLastLoadedResourceState(state.listView.listResourceState); - - return { - pageIndex: state.location.page_index, - pageSize: state.location.page_size, - totalItemCount: lastLoadedResourceState?.data.totalItemsCount || 0, - pageSizeOptions: [...MANAGEMENT_PAGE_SIZE_OPTIONS], - }; -}; - -export const getCurrentLocation = ( - state: Immutable -): TrustedAppsListPageLocation => state.location; - -export const getListErrorMessage = ( - state: Immutable -): string | undefined => { - return getCurrentResourceError(state.listView.listResourceState)?.message; -}; - -export const isListLoading = (state: Immutable): boolean => { - return isLoadingResourceState(state.listView.listResourceState); -}; - -export const isDeletionDialogOpen = (state: Immutable): boolean => { - return state.deletionDialog.entry !== undefined; -}; - -export const isDeletionInProgress = (state: Immutable): boolean => { - return isLoadingResourceState(state.deletionDialog.submissionResourceState); -}; - -export const isDeletionSuccessful = (state: Immutable): boolean => { - return isLoadedResourceState(state.deletionDialog.submissionResourceState); -}; - -export const getDeletionError = ( - state: Immutable -): Immutable | undefined => { - const submissionResourceState = state.deletionDialog.submissionResourceState; - - return isFailedResourceState(submissionResourceState) ? submissionResourceState.error : undefined; -}; - -export const getDeletionSubmissionResourceState = ( - state: Immutable -): AsyncResourceState => { - return state.deletionDialog.submissionResourceState; -}; - -export const getDeletionDialogEntry = ( - state: Immutable -): Immutable | undefined => { - return state.deletionDialog.entry; -}; - -export const isCreationDialogLocation = (state: Immutable): boolean => { - return !!state.location.show; -}; - -export const getCreationSubmissionResourceState = ( - state: Immutable -): Immutable> => { - return state.creationDialog.submissionResourceState; -}; - -export const getCreationDialogFormEntry = ( - state: Immutable -): Immutable | undefined => { - return state.creationDialog.formState?.entry; -}; - -export const isCreationDialogFormValid = (state: Immutable): boolean => { - return state.creationDialog.formState?.isValid || false; -}; - -export const isCreationInProgress = (state: Immutable): boolean => { - return isLoadingResourceState(state.creationDialog.submissionResourceState); -}; - -export const isCreationSuccessful = (state: Immutable): boolean => { - return isLoadedResourceState(state.creationDialog.submissionResourceState); -}; - -export const getCreationError = ( - state: Immutable -): Immutable | undefined => { - const submissionResourceState = state.creationDialog.submissionResourceState; - - return isFailedResourceState(submissionResourceState) ? submissionResourceState.error : undefined; -}; - -export const entriesExistState: ( - state: Immutable -) => Immutable = (state) => state.entriesExist; - -export const checkingIfEntriesExist: (state: Immutable) => boolean = - createSelector(entriesExistState, (doEntriesExists) => { - return !isLoadedResourceState(doEntriesExists); - }); - -export const entriesExist: (state: Immutable) => boolean = createSelector( - entriesExistState, - (doEntriesExists) => { - return isLoadedResourceState(doEntriesExists) && doEntriesExists.data; - } -); - -export const prevEntriesExist: (state: Immutable) => boolean = - createSelector(entriesExistState, (doEntriesExists) => { - return ( - isLoadingResourceState(doEntriesExists) && !!getLastLoadedResourceState(doEntriesExists)?.data - ); - }); - -export const trustedAppsListPageActive: (state: Immutable) => boolean = ( - state -) => state.active; - -export const policiesState = ( - state: Immutable -): Immutable => state.policies; - -export const loadingPolicies: (state: Immutable) => boolean = - createSelector(policiesState, (policies) => isLoadingResourceState(policies)); - -export const listOfPolicies: ( - state: Immutable -) => Immutable = createSelector(policiesState, (policies) => { - return isLoadedResourceState(policies) ? policies.data.items : []; -}); - -export const isLoadingListOfPolicies: (state: Immutable) => boolean = - createSelector(policiesState, (policies) => { - return isLoadingResourceState(policies); - }); - -export const getMapOfPoliciesById: ( - state: Immutable -) => Immutable>> = createSelector( - listOfPolicies, - (policies) => { - return policies.reduce>>((mapById, policy) => { - mapById[policy.id] = policy; - return mapById; - }, {}) as Immutable>>; - } -); - -export const isEdit: (state: Immutable) => boolean = createSelector( - getCurrentLocation, - ({ show }) => { - return show === 'edit'; - } -); - -export const editItemId: (state: Immutable) => string | undefined = - createSelector(getCurrentLocation, ({ id }) => { - return id; - }); - -export const editItemState: ( - state: Immutable -) => Immutable['creationDialog']['editItem'] = (state) => { - return state.creationDialog.editItem; -}; - -export const isFetchingEditTrustedAppItem: (state: Immutable) => boolean = - createSelector(editItemState, (editTrustedAppState) => { - return editTrustedAppState ? isLoadingResourceState(editTrustedAppState) : false; - }); - -export const editTrustedAppFetchError: ( - state: Immutable -) => ServerApiError | undefined = createSelector(editItemState, (itemForEditState) => { - return itemForEditState && getCurrentResourceError(itemForEditState); -}); - -export const editingTrustedApp: ( - state: Immutable -) => undefined | Immutable = createSelector(editItemState, (editTrustedAppState) => { - if (editTrustedAppState && isLoadedResourceState(editTrustedAppState)) { - return editTrustedAppState.data; - } -}); diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/test_utils/index.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/test_utils/index.ts deleted file mode 100644 index 32e1867db567..000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/test_utils/index.ts +++ /dev/null @@ -1,168 +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 { combineReducers, createStore } from 'redux'; -import { OperatingSystem } from '@kbn/securitysolution-utils'; -import { TrustedApp } from '../../../../../common/endpoint/types'; -import { RoutingAction } from '../../../../common/store/routing'; - -import { - MANAGEMENT_DEFAULT_PAGE, - MANAGEMENT_DEFAULT_PAGE_SIZE, - MANAGEMENT_PAGE_SIZE_OPTIONS, - MANAGEMENT_STORE_GLOBAL_NAMESPACE, - MANAGEMENT_STORE_TRUSTED_APPS_NAMESPACE, -} from '../../../common/constants'; - -import { - AsyncResourceState, - FailedResourceState, - LoadedResourceState, - LoadingResourceState, - Pagination, - StaleResourceState, - TrustedAppsListData, - TrustedAppsListPageState, - UninitialisedResourceState, -} from '../state'; - -import { trustedAppsPageReducer } from '../store/reducer'; -import { TrustedAppsListResourceStateChanged } from '../store/action'; - -const OPERATING_SYSTEMS: OperatingSystem[] = [ - OperatingSystem.WINDOWS, - OperatingSystem.MAC, - OperatingSystem.LINUX, -]; - -const generate = (count: number, generator: (i: number) => T) => - [...new Array(count).keys()].map(generator); - -export const createSampleTrustedApp = (i: number, longTexts?: boolean): TrustedApp => { - return { - id: String(i), - version: 'abc123', - name: generate(longTexts ? 10 : 1, () => `trusted app ${i}`).join(' '), - description: generate(longTexts ? 10 : 1, () => `Trusted App ${i}`).join(' '), - created_at: '1 minute ago', - created_by: 'someone', - updated_at: '1 minute ago', - updated_by: 'someone', - os: OPERATING_SYSTEMS[i % 3], - entries: [], - effectScope: { type: 'global' }, - }; -}; - -export const createSampleTrustedApps = ( - pagination: Partial, - longTexts?: boolean -): TrustedApp[] => { - const fullPagination = { ...createDefaultPagination(), ...pagination }; - - return generate(fullPagination.pageSize, (i: number) => createSampleTrustedApp(i, longTexts)); -}; - -export const createTrustedAppsListData = ( - pagination: Partial, - timestamp: number, - longTexts?: boolean -) => { - const fullPagination = { ...createDefaultPagination(), ...pagination }; - - return { - items: createSampleTrustedApps(fullPagination, longTexts), - pageSize: fullPagination.pageSize, - pageIndex: fullPagination.pageIndex, - totalItemsCount: fullPagination.totalItemCount, - timestamp, - filter: '', - includedPolicies: '', - }; -}; - -export const createServerApiError = (message: string) => ({ - statusCode: 500, - error: 'Internal Server Error', - message, -}); - -export const createUninitialisedResourceState = (): UninitialisedResourceState => ({ - type: 'UninitialisedResourceState', -}); - -export const createListLoadedResourceState = ( - pagination: Partial, - timestamp: number, - longTexts?: boolean -): LoadedResourceState => ({ - type: 'LoadedResourceState', - data: createTrustedAppsListData(pagination, timestamp, longTexts), -}); - -export const createListFailedResourceState = ( - message: string, - lastLoadedState?: LoadedResourceState -): FailedResourceState => ({ - type: 'FailedResourceState', - error: createServerApiError(message), - lastLoadedState, -}); - -export const createListLoadingResourceState = ( - previousState: StaleResourceState = createUninitialisedResourceState() -): LoadingResourceState => ({ - type: 'LoadingResourceState', - previousState, -}); - -export const createListComplexLoadingResourceState = ( - pagination: Partial, - timestamp: number -): LoadingResourceState => - createListLoadingResourceState( - createListFailedResourceState( - 'Internal Server Error', - createListLoadedResourceState(pagination, timestamp) - ) - ); - -export const createDefaultPagination = (): Pagination => ({ - pageIndex: MANAGEMENT_DEFAULT_PAGE, - pageSize: MANAGEMENT_DEFAULT_PAGE_SIZE, - totalItemCount: 200, - pageSizeOptions: [...MANAGEMENT_PAGE_SIZE_OPTIONS], -}); - -export const createLoadedListViewWithPagination = ( - freshDataTimestamp: number, - pagination: Partial = createDefaultPagination() -): TrustedAppsListPageState['listView'] => ({ - listResourceState: createListLoadedResourceState(pagination, freshDataTimestamp), - freshDataTimestamp, -}); - -export const createUserChangedUrlAction = (path: string, search: string = ''): RoutingAction => { - return { type: 'userChangedUrl', payload: { pathname: path, search, hash: '' } }; -}; - -export const createTrustedAppsListResourceStateChangedAction = ( - newState: AsyncResourceState -): TrustedAppsListResourceStateChanged => ({ - type: 'trustedAppsListResourceStateChanged', - payload: { newState }, -}); - -export const createGlobalNoMiddlewareStore = () => { - return createStore( - combineReducers({ - [MANAGEMENT_STORE_GLOBAL_NAMESPACE]: combineReducers({ - [MANAGEMENT_STORE_TRUSTED_APPS_NAMESPACE]: trustedAppsPageReducer, - }), - }) - ); -}; diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/__snapshots__/trusted_app_deletion_dialog.test.tsx.snap b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/__snapshots__/trusted_app_deletion_dialog.test.tsx.snap deleted file mode 100644 index 070f1b9eabe2..000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/__snapshots__/trusted_app_deletion_dialog.test.tsx.snap +++ /dev/null @@ -1,414 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`TrustedAppDeletionDialog renders correctly initially 1`] = ` - -
- -`; - -exports[`TrustedAppDeletionDialog renders correctly when deletion failed 1`] = ` - -
-
-
-
- -
-
-
- Delete " - - trusted app 3 - - " -
-
-
-
-
-
-
-
-
-

- Deleting this entry will remove it from all associated policies. -

-
-
-
-
-
-

- This action cannot be undone. Are you sure you wish to continue? -

-
-
-
-
- - -
-
-
-
-
- -`; - -exports[`TrustedAppDeletionDialog renders correctly when deletion is in progress 1`] = ` - -
-
-
-
- -
-
-
- Delete " - - trusted app 3 - - " -
-
-
-
-
-
-
-
-
-

- Deleting this entry will remove it from all associated policies. -

-
-
-
-
-
-

- This action cannot be undone. Are you sure you wish to continue? -

-
-
-
-
- - -
-
-
-
-
- -`; - -exports[`TrustedAppDeletionDialog renders correctly when dialog started 1`] = ` - -
-
-
-
- -
-
-
- Delete " - - trusted app 3 - - " -
-
-
-
-
-
-
-
-
-

- Deleting this entry will remove it from all associated policies. -

-
-
-
-
-
-

- This action cannot be undone. Are you sure you wish to continue? -

-
-
-
-
- - -
-
-
-
-
- -`; diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/create_trusted_app_flyout.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/create_trusted_app_flyout.tsx deleted file mode 100644 index f76ac89474e7..000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/create_trusted_app_flyout.tsx +++ /dev/null @@ -1,283 +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 { - EuiButton, - EuiButtonEmpty, - EuiCallOut, - EuiFlexGroup, - EuiFlexItem, - EuiFlyout, - EuiFlyoutBody, - EuiFlyoutFooter, - EuiFlyoutHeader, - EuiLink, - EuiSpacer, - EuiText, - EuiTitle, -} from '@elastic/eui'; -import React, { memo, useCallback, useEffect, useState, useMemo } from 'react'; -import { EuiFlyoutProps } from '@elastic/eui/src/components/flyout/flyout'; -import { FormattedMessage } from '@kbn/i18n-react'; -import { useDispatch } from 'react-redux'; -import { useHistory } from 'react-router-dom'; -import { i18n } from '@kbn/i18n'; -import _ from 'lodash'; -import { CreateTrustedAppForm, CreateTrustedAppFormProps } from './create_trusted_app_form'; -import { - editTrustedAppFetchError, - getCreationDialogFormEntry, - getCreationError, - getCurrentLocation, - isCreationDialogFormValid, - isCreationInProgress, - isCreationSuccessful, - isEdit, - listOfPolicies, - loadingPolicies, -} from '../../store/selectors'; -import { AppAction } from '../../../../../common/store/actions'; -import { useTrustedAppsSelector } from '../hooks'; - -import { ABOUT_TRUSTED_APPS } from '../translations'; -import { defaultNewTrustedApp } from '../../store/builders'; -import { getTrustedAppsListPath } from '../../../../common/routing'; -import { useKibana, useToasts } from '../../../../../common/lib/kibana'; -import { useTestIdGenerator } from '../../../../components/hooks/use_test_id_generator'; -import { useLicense } from '../../../../../common/hooks/use_license'; -import { isGlobalEffectScope } from '../../state/type_guards'; -import { NewTrustedApp } from '../../../../../../common/endpoint/types'; - -export type CreateTrustedAppFlyoutProps = Omit; -export const CreateTrustedAppFlyout = memo( - ({ onClose, ...flyoutProps }) => { - const dispatch = useDispatch<(action: AppAction) => void>(); - const history = useHistory(); - const toasts = useToasts(); - - const creationInProgress = useTrustedAppsSelector(isCreationInProgress); - const creationErrors = useTrustedAppsSelector(getCreationError); - const creationSuccessful = useTrustedAppsSelector(isCreationSuccessful); - const isFormValid = useTrustedAppsSelector(isCreationDialogFormValid); - const isLoadingPolicies = useTrustedAppsSelector(loadingPolicies); - const policyList = useTrustedAppsSelector(listOfPolicies); - const isEditMode = useTrustedAppsSelector(isEdit); - const trustedAppFetchError = useTrustedAppsSelector(editTrustedAppFetchError); - const formValues = useTrustedAppsSelector(getCreationDialogFormEntry) || defaultNewTrustedApp(); - const location = useTrustedAppsSelector(getCurrentLocation); - const isPlatinumPlus = useLicense().isPlatinumPlus(); - const docLinks = useKibana().services.docLinks; - const [isFormDirty, setIsFormDirty] = useState(false); - - const dataTestSubj = flyoutProps['data-test-subj']; - - const policies = useMemo(() => { - return { - // Casting is needed due to the use of `Immutable<>` on the return value from the selector above - options: policyList as CreateTrustedAppFormProps['policies']['options'], - isLoading: isLoadingPolicies, - }; - }, [isLoadingPolicies, policyList]); - - const creationErrorsMessage = useMemo(() => { - return creationErrors?.message ?? []; - }, [creationErrors]); - - const getTestId = useTestIdGenerator(dataTestSubj); - - const handleCancelClick = useCallback(() => { - if (creationInProgress) { - return; - } - onClose(); - }, [onClose, creationInProgress]); - - const handleSaveClick = useCallback( - () => dispatch({ type: 'trustedAppCreationDialogConfirmed' }), - [dispatch] - ); - - const handleFormOnChange = useCallback( - (newFormState) => { - dispatch({ - type: 'trustedAppCreationDialogFormStateUpdated', - payload: { entry: newFormState.item, isValid: newFormState.isValid }, - }); - if (_.isEqual(formValues, newFormState.item) === false) { - setIsFormDirty(true); - } - }, - - [dispatch, formValues] - ); - - const [wasByPolicy, setWasByPolicy] = useState(!isGlobalEffectScope(formValues.effectScope)); - // set initial state of `wasByPolicy` that checks if the initial state of the exception was by policy or not - useEffect(() => { - if (!isFormDirty && formValues.effectScope) { - setWasByPolicy(!isGlobalEffectScope(formValues.effectScope)); - } - }, [isFormDirty, formValues.effectScope]); - - const isGlobal = useMemo(() => { - return isGlobalEffectScope((formValues as NewTrustedApp).effectScope); - }, [formValues]); - - const showExpiredLicenseBanner = useMemo(() => { - return !isPlatinumPlus && isEditMode && wasByPolicy && (!isGlobal || isFormDirty); - }, [isPlatinumPlus, isEditMode, isGlobal, isFormDirty, wasByPolicy]); - - // If there was a failure trying to retrieve the Trusted App for edit item, - // then redirect back to the list ++ show toast message. - useEffect(() => { - if (trustedAppFetchError) { - // Replace the current URL route so that user does not keep hitting this page via browser back/fwd buttons - history.replace( - getTrustedAppsListPath({ - ...location, - show: undefined, - id: undefined, - }) - ); - - toasts.addWarning( - i18n.translate( - 'xpack.securitySolution.trustedapps.createTrustedAppFlyout.notFoundToastMessage', - { - defaultMessage: 'Unable to edit trusted application ({apiMsg})', - values: { - apiMsg: trustedAppFetchError.message, - }, - } - ) - ); - } - }, [history, location, toasts, trustedAppFetchError]); - - // If it was created, then close flyout - useEffect(() => { - if (creationSuccessful) { - onClose(); - } - }, [onClose, creationSuccessful]); - - return ( - - - -

- {isEditMode ? ( - - ) : ( - - )} -

-
-
- {showExpiredLicenseBanner && ( - - - - - - - )} - - -

- {i18n.translate('xpack.securitySolution.trustedApps.detailsSectionTitle', { - defaultMessage: 'Details', - })} -

-
- - {!isEditMode && ( - <> - -

{ABOUT_TRUSTED_APPS}

-
- - - )} - -
- - - - - - - - - - - {isEditMode ? ( - - ) : ( - - )} - - - - -
- ); - } -); - -CreateTrustedAppFlyout.displayName = 'NewTrustedAppFlyout'; diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/create_trusted_app_form.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/create_trusted_app_form.tsx deleted file mode 100644 index 8e7246962b5e..000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/create_trusted_app_form.tsx +++ /dev/null @@ -1,623 +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 React, { ChangeEventHandler, memo, useCallback, useEffect, useMemo, useState } from 'react'; -import { - EuiFieldText, - EuiForm, - EuiFormRow, - EuiHorizontalRule, - EuiSuperSelect, - EuiSuperSelectOption, - EuiTextArea, - EuiText, - EuiSpacer, -} from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { - hasSimpleExecutableName, - isPathValid, - ConditionEntryField, - OperatingSystem, -} from '@kbn/securitysolution-utils'; -import { EuiFormProps } from '@elastic/eui/src/components/form/form'; - -import { - TrustedAppConditionEntry, - EffectScope, - MacosLinuxConditionEntry, - MaybeImmutable, - NewTrustedApp, -} from '../../../../../../common/endpoint/types'; -import { - isValidHash, - getDuplicateFields, -} from '../../../../../../common/endpoint/service/trusted_apps/validations'; - -import { - isGlobalEffectScope, - isMacosLinuxTrustedAppCondition, - isPolicyEffectScope, - isWindowsTrustedAppCondition, -} from '../../state/type_guards'; -import { defaultConditionEntry } from '../../store/builders'; -import { CONDITION_FIELD_TITLE, OS_TITLES } from '../translations'; -import { LogicalConditionBuilder, LogicalConditionBuilderProps } from './logical_condition'; -import { useTestIdGenerator } from '../../../../components/hooks/use_test_id_generator'; -import { useLicense } from '../../../../../common/hooks/use_license'; -import { - EffectedPolicySelect, - EffectedPolicySelection, - EffectedPolicySelectProps, -} from '../../../../components/effected_policy_select'; - -const OPERATING_SYSTEMS: readonly OperatingSystem[] = [ - OperatingSystem.MAC, - OperatingSystem.WINDOWS, - OperatingSystem.LINUX, -]; - -interface FieldValidationState { - /** If this fields state is invalid. Drives display of errors on the UI */ - isInvalid: boolean; - errors: React.ReactNode[]; - warnings: React.ReactNode[]; -} -interface ValidationResult { - /** Overall indicator if form is valid */ - isValid: boolean; - - /** Individual form field validations */ - result: Partial<{ - [key in keyof NewTrustedApp]: FieldValidationState; - }>; -} - -const addResultToValidation = ( - validation: ValidationResult, - field: keyof NewTrustedApp, - type: 'warnings' | 'errors', - resultValue: React.ReactNode -) => { - if (!validation.result[field]) { - validation.result[field] = { - isInvalid: false, - errors: [], - warnings: [], - }; - } - const errorMarkup: React.ReactNode = type === 'warnings' ?
{resultValue}
: resultValue; - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - validation.result[field]![type].push(errorMarkup); - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - validation.result[field]!.isInvalid = true; -}; - -const validateFormValues = (values: MaybeImmutable): ValidationResult => { - let isValid: ValidationResult['isValid'] = true; - const validation: ValidationResult = { - isValid, - result: {}, - }; - - // Name field - if (!values.name.trim()) { - isValid = false; - addResultToValidation( - validation, - 'name', - 'errors', - i18n.translate('xpack.securitySolution.trustedapps.create.nameRequiredMsg', { - defaultMessage: 'Name is required', - }) - ); - } - - if (!values.os) { - isValid = false; - addResultToValidation( - validation, - 'os', - 'errors', - i18n.translate('xpack.securitySolution.trustedapps.create.osRequiredMsg', { - defaultMessage: 'Operating System is required', - }) - ); - } - - if (!values.entries.length) { - isValid = false; - addResultToValidation( - validation, - 'entries', - 'errors', - i18n.translate('xpack.securitySolution.trustedapps.create.conditionRequiredMsg', { - defaultMessage: 'At least one Field definition is required', - }) - ); - } else { - const duplicated = getDuplicateFields(values.entries as TrustedAppConditionEntry[]); - if (duplicated.length) { - isValid = false; - duplicated.forEach((field) => { - addResultToValidation( - validation, - 'entries', - 'errors', - i18n.translate('xpack.securitySolution.trustedapps.create.conditionFieldDuplicatedMsg', { - defaultMessage: '{field} cannot be added more than once', - values: { field: CONDITION_FIELD_TITLE[field] }, - }) - ); - }); - } - values.entries.forEach((entry, index) => { - const isValidPathEntry = isPathValid({ - os: values.os, - field: entry.field, - type: entry.type, - value: entry.value, - }); - - if (!entry.field || !entry.value.trim()) { - isValid = false; - addResultToValidation( - validation, - 'entries', - 'errors', - i18n.translate( - 'xpack.securitySolution.trustedapps.create.conditionFieldValueRequiredMsg', - { - defaultMessage: '[{row}] Field entry must have a value', - values: { row: index + 1 }, - } - ) - ); - } else if (entry.field === ConditionEntryField.HASH && !isValidHash(entry.value)) { - isValid = false; - addResultToValidation( - validation, - 'entries', - 'errors', - i18n.translate('xpack.securitySolution.trustedapps.create.conditionFieldInvalidHashMsg', { - defaultMessage: '[{row}] Invalid hash value', - values: { row: index + 1 }, - }) - ); - } else if (!isValidPathEntry) { - addResultToValidation( - validation, - 'entries', - 'warnings', - i18n.translate('xpack.securitySolution.trustedapps.create.conditionFieldInvalidPathMsg', { - defaultMessage: '[{row}] Path may be formed incorrectly; verify value', - values: { row: index + 1 }, - }) - ); - } else if ( - isValidPathEntry && - !hasSimpleExecutableName({ os: values.os, value: entry.value, type: entry.type }) - ) { - addResultToValidation( - validation, - 'entries', - 'warnings', - i18n.translate( - 'xpack.securitySolution.trustedapps.create.conditionFieldDegradedPerformanceMsg', - { - defaultMessage: `[{row}] A wildcard in the filename will affect the endpoint's performance`, - values: { row: index + 1 }, - } - ) - ); - } - }); - } - - validation.isValid = isValid; - return validation; -}; - -export interface TrustedAppFormState { - isValid: boolean; - item: NewTrustedApp; -} - -export type CreateTrustedAppFormProps = Pick< - EuiFormProps, - 'className' | 'data-test-subj' | 'isInvalid' | 'error' | 'invalidCallout' -> & { - /** The trusted app values that will be passed to the form */ - trustedApp: MaybeImmutable; - isEditMode: boolean; - isDirty: boolean; - wasByPolicy: boolean; - onChange: (state: TrustedAppFormState) => void; - /** Setting passed on to the EffectedPolicySelect component */ - policies: Pick; - /** if form should be shown full width of parent container */ - fullWidth?: boolean; -}; -export const CreateTrustedAppForm = memo( - ({ - fullWidth, - isEditMode, - isDirty, - wasByPolicy, - onChange, - trustedApp: _trustedApp, - policies = { options: [] }, - ...formProps - }) => { - const trustedApp = _trustedApp as NewTrustedApp; - - const dataTestSubj = formProps['data-test-subj']; - - const isPlatinumPlus = useLicense().isPlatinumPlus(); - - const isGlobal = useMemo(() => { - return isGlobalEffectScope(trustedApp.effectScope); - }, [trustedApp]); - - const showAssignmentSection = useMemo(() => { - return isPlatinumPlus || (isEditMode && (!isGlobal || (wasByPolicy && isGlobal && isDirty))); - }, [isEditMode, isGlobal, isDirty, isPlatinumPlus, wasByPolicy]); - - const osOptions: Array> = useMemo( - () => OPERATING_SYSTEMS.map((os) => ({ value: os, inputDisplay: OS_TITLES[os] })), - [] - ); - - // We create local state for the list of policies because we want the selected policies to - // persist while the user is on the form and possibly toggling between global/non-global - const [selectedPolicies, setSelectedPolicies] = useState({ - isGlobal, - selected: [], - }); - - const [validationResult, setValidationResult] = useState(() => - validateFormValues(trustedApp) - ); - - const [wasVisited, setWasVisited] = useState< - Partial<{ - [key in keyof NewTrustedApp]: boolean; - }> - >({}); - - const getTestId = useTestIdGenerator(dataTestSubj); - - const notifyOfChange = useCallback( - (updatedFormValues: TrustedAppFormState['item']) => { - const updatedValidationResult = validateFormValues(updatedFormValues); - - setValidationResult(updatedValidationResult); - - onChange({ - item: updatedFormValues, - isValid: updatedValidationResult.isValid, - }); - }, - [onChange] - ); - - const handleAndClick = useCallback(() => { - if (trustedApp.os === OperatingSystem.WINDOWS) { - notifyOfChange({ - ...trustedApp, - entries: [...trustedApp.entries, defaultConditionEntry()].filter( - isWindowsTrustedAppCondition - ), - }); - } else { - notifyOfChange({ - ...trustedApp, - entries: [ - ...trustedApp.entries.filter(isMacosLinuxTrustedAppCondition), - defaultConditionEntry(), - ], - }); - } - }, [notifyOfChange, trustedApp]); - - const handleDomChangeEvents = useCallback< - ChangeEventHandler - >( - ({ target: { name, value } }) => { - notifyOfChange({ - ...trustedApp, - [name]: value, - }); - }, - [notifyOfChange, trustedApp] - ); - - // Handles keeping track if an input form field has been visited - const handleDomBlurEvents = useCallback>( - ({ target: { name } }) => { - setWasVisited((prevState) => { - return { - ...prevState, - [name]: true, - }; - }); - }, - [] - ); - - const handleOsChange = useCallback<(v: OperatingSystem) => void>( - (newOsValue) => { - setWasVisited((prevState) => { - return { - ...prevState, - os: true, - }; - }); - - const updatedState: NewTrustedApp = { - ...trustedApp, - entries: [], - os: newOsValue, - }; - if (updatedState.os !== OperatingSystem.WINDOWS) { - updatedState.entries.push( - ...(trustedApp.entries.filter((entry) => - isMacosLinuxTrustedAppCondition(entry) - ) as MacosLinuxConditionEntry[]) - ); - if (updatedState.entries.length === 0) { - updatedState.entries.push(defaultConditionEntry()); - } - } else { - updatedState.entries.push(...trustedApp.entries); - } - - notifyOfChange(updatedState); - }, - [notifyOfChange, trustedApp] - ); - - const handleEntryRemove = useCallback( - (entry: NewTrustedApp['entries'][0]) => { - notifyOfChange({ - ...trustedApp, - entries: trustedApp.entries.filter((item) => item !== entry), - } as NewTrustedApp); - }, - [notifyOfChange, trustedApp] - ); - - const handleEntryChange = useCallback( - (newEntry, oldEntry) => { - if (trustedApp.os === OperatingSystem.WINDOWS) { - notifyOfChange({ - ...trustedApp, - entries: trustedApp.entries.map((item) => { - if (item === oldEntry) { - return newEntry; - } - return item; - }), - } as NewTrustedApp); - } else { - notifyOfChange({ - ...trustedApp, - entries: trustedApp.entries.map((item) => { - if (item === oldEntry) { - return newEntry; - } - return item; - }), - } as NewTrustedApp); - } - }, - [notifyOfChange, trustedApp] - ); - - const handleConditionBuilderOnVisited: LogicalConditionBuilderProps['onVisited'] = - useCallback(() => { - setWasVisited((prevState) => { - return { - ...prevState, - entries: true, - }; - }); - }, []); - - const handlePolicySelectChange: EffectedPolicySelectProps['onChange'] = useCallback( - (selection) => { - setSelectedPolicies(() => selection); - - let newEffectedScope: EffectScope; - - if (selection.isGlobal) { - newEffectedScope = { - type: 'global', - }; - } else { - newEffectedScope = { - type: 'policy', - policies: selection.selected.map((policy) => policy.id), - }; - } - - notifyOfChange({ - ...trustedApp, - effectScope: newEffectedScope, - }); - }, - [notifyOfChange, trustedApp] - ); - - // Anytime the form values change, re-validate - useEffect(() => { - setValidationResult((prevState) => { - const newResults = validateFormValues(trustedApp); - - // Only notify if the overall validation result is different - if (newResults.isValid !== prevState.isValid) { - notifyOfChange(trustedApp); - } - - return newResults; - }); - }, [notifyOfChange, trustedApp]); - - // Anytime the TrustedApp has an effective scope of `policies`, then ensure that - // those polices are selected in the UI while at the same time preserving prior - // selections (UX requirement) - useEffect(() => { - setSelectedPolicies((currentSelection) => { - if (isPolicyEffectScope(trustedApp.effectScope) && policies.options.length > 0) { - const missingSelectedPolicies: EffectedPolicySelectProps['selected'] = []; - - for (const policyId of trustedApp.effectScope.policies) { - if ( - !currentSelection.selected.find( - (currentlySelectedPolicyItem) => currentlySelectedPolicyItem.id === policyId - ) - ) { - const newSelectedPolicy = policies.options.find((policy) => policy.id === policyId); - if (newSelectedPolicy) { - missingSelectedPolicies.push(newSelectedPolicy); - } - } - } - - if (missingSelectedPolicies.length) { - return { - ...currentSelection, - selected: [...currentSelection.selected, ...missingSelectedPolicies], - }; - } - } - - return currentSelection; - }); - }, [policies.options, trustedApp.effectScope]); - - return ( - - - - - - - - - -

- {i18n.translate('xpack.securitySolution.trustedApps.conditionsSectionTitle', { - defaultMessage: 'Conditions', - })} -

-
- - -

- {i18n.translate('xpack.securitySolution.trustedApps.conditionsSectionDescription', { - defaultMessage: - 'Select an operating system and add conditions. Availability of conditions may depend on your chosen OS.', - })} -

-
- - - - - - - - {showAssignmentSection ? ( - <> - - - - - - ) : null} -
- ); - } -); - -CreateTrustedAppForm.displayName = 'NewTrustedAppForm'; diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/create_trusted_app_form.test.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/form.test.tsx similarity index 52% rename from x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/create_trusted_app_form.test.tsx rename to x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/form.test.tsx index 68dd43fa4115..dca86557f630 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/create_trusted_app_form.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/form.test.tsx @@ -6,25 +6,30 @@ */ import React from 'react'; -import * as reactTestingLibrary from '@testing-library/react'; -import { fireEvent, getByTestId } from '@testing-library/dom'; +import { screen, cleanup, act, fireEvent, getByTestId } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { + TrustedAppEntryTypes, + OperatingSystem, + ConditionEntryField, +} from '@kbn/securitysolution-utils'; +import { ENDPOINT_TRUSTED_APPS_LIST_ID } from '@kbn/securitysolution-list-constants'; -import { ConditionEntryField, OperatingSystem } from '@kbn/securitysolution-utils'; -import { NewTrustedApp } from '../../../../../../common/endpoint/types'; +import { TrustedAppsForm } from './form'; +import { + ArtifactFormComponentOnChangeCallbackProps, + ArtifactFormComponentProps, +} from '../../../../components/artifact_list_page'; import { AppContextTestRender, createAppRootMockRenderer, } from '../../../../../common/mock/endpoint'; - -import { CreateTrustedAppForm, CreateTrustedAppFormProps } from './create_trusted_app_form'; -import { defaultNewTrustedApp } from '../../store/builders'; -import { EndpointDocGenerator } from '../../../../../../common/endpoint/generate_data'; -import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features'; +import { INPUT_ERRORS } from '../translations'; import { licenseService } from '../../../../../common/hooks/use_license'; import { forceHTMLElementOffsetWidth } from '../../../../components/effected_policy_select/test_utils'; +import type { PolicyData, TrustedAppConditionEntry } from '../../../../../../common/endpoint/types'; -jest.mock('../../../../../common/hooks/use_experimental_features'); -const useIsExperimentalFeatureEnabledMock = useIsExperimentalFeatureEnabled as jest.Mock; +import { EndpointDocGenerator } from '../../../../../../common/endpoint/generate_data'; jest.mock('../../../../../common/hooks/use_license', () => { const licenseServiceInstance = { @@ -38,55 +43,108 @@ jest.mock('../../../../../common/hooks/use_license', () => { }; }); -describe('When using the Trusted App Form', () => { - const dataTestSubjForForm = 'createForm'; +describe('Trusted apps form', () => { + const formPrefix = 'trustedApps-form'; const generator = new EndpointDocGenerator('effected-policy-select'); - let resetHTMLElementOffsetWidth: ReturnType; + let formProps: jest.Mocked; let mockedContext: AppContextTestRender; - let formProps: jest.Mocked; let renderResult: ReturnType; + let latestUpdatedItem: ArtifactFormComponentProps['item']; - // As the form's `onChange()` callback is executed, this variable will - // hold the latest updated trusted app. Use it to re-render - let latestUpdatedTrustedApp: NewTrustedApp; - - const getUI = () => ; + const getUI = () => ; const render = () => { return (renderResult = mockedContext.render(getUI())); }; const rerender = () => renderResult.rerender(getUI()); - const rerenderWithLatestTrustedApp = () => { - formProps.trustedApp = latestUpdatedTrustedApp; + const rerenderWithLatestProps = () => { + formProps.item = latestUpdatedItem; rerender(); }; + function createEntry( + field: T, + type: TrustedAppEntryTypes, + value: string + ): TrustedAppConditionEntry { + return { + field, + type, + operator: 'included', + value, + }; + } + + function createItem( + overrides: Partial = {} + ): ArtifactFormComponentProps['item'] { + const defaults: ArtifactFormComponentProps['item'] = { + list_id: ENDPOINT_TRUSTED_APPS_LIST_ID, + name: '', + description: '', + os_types: [OperatingSystem.WINDOWS], + entries: [createEntry(ConditionEntryField.HASH, 'match', '')], + type: 'simple', + tags: ['policy:all'], + }; + return { + ...defaults, + ...overrides, + }; + } + + function createOnChangeArgs( + overrides: Partial + ): ArtifactFormComponentOnChangeCallbackProps { + const defaults = { + item: createItem(), + isValid: false, + }; + return { + ...defaults, + ...overrides, + }; + } + + function createPolicies(): PolicyData[] { + const policies = [ + generator.generatePolicyPackagePolicy(), + generator.generatePolicyPackagePolicy(), + ]; + policies.map((p, i) => { + p.id = `id-${i}`; + p.name = `some-policy-${Math.random().toString(36).split('.').pop()}`; + return p; + }); + return policies; + } + // Some helpers const setTextFieldValue = (textField: HTMLInputElement | HTMLTextAreaElement, value: string) => { - reactTestingLibrary.act(() => { + act(() => { fireEvent.change(textField, { target: { value }, }); fireEvent.blur(textField); }); }; - const getNameField = (dataTestSub: string = dataTestSubjForForm): HTMLInputElement => { + const getDetailsBlurb = (dataTestSub: string = formPrefix): HTMLInputElement => { + return renderResult.queryByTestId(`${dataTestSub}-about`) as HTMLInputElement; + }; + const getNameField = (dataTestSub: string = formPrefix): HTMLInputElement => { return renderResult.getByTestId(`${dataTestSub}-nameTextField`) as HTMLInputElement; }; - const getOsField = (dataTestSub: string = dataTestSubjForForm): HTMLButtonElement => { + const getOsField = (dataTestSub: string = formPrefix): HTMLButtonElement => { return renderResult.getByTestId(`${dataTestSub}-osSelectField`) as HTMLButtonElement; }; - const getDescriptionField = (dataTestSub: string = dataTestSubjForForm): HTMLTextAreaElement => { + const getDescriptionField = (dataTestSub: string = formPrefix): HTMLTextAreaElement => { return renderResult.getByTestId(`${dataTestSub}-descriptionField`) as HTMLTextAreaElement; }; - const getCondition = ( - index: number = 0, - dataTestSub: string = dataTestSubjForForm - ): HTMLElement => { + const getCondition = (index: number = 0, dataTestSub: string = formPrefix): HTMLElement => { return renderResult.getByTestId(`${dataTestSub}-conditionsBuilder-group1-entry${index}`); }; - const getAllConditions = (dataTestSub: string = dataTestSubjForForm): HTMLElement[] => { + const getAllConditions = (dataTestSub: string = formPrefix): HTMLElement[] => { return Array.from( renderResult.getByTestId(`${dataTestSub}-conditionsBuilder-group1-entries`).children ) as HTMLElement[]; @@ -97,19 +155,16 @@ describe('When using the Trusted App Form', () => { const getConditionFieldSelect = (condition: HTMLElement): HTMLButtonElement => { return getByTestId(condition, `${condition.dataset.testSubj}-field`) as HTMLButtonElement; }; + const getConditionValue = (condition: HTMLElement): HTMLInputElement => { return getByTestId(condition, `${condition.dataset.testSubj}-value`) as HTMLInputElement; }; - const getConditionBuilderAndButton = ( - dataTestSub: string = dataTestSubjForForm - ): HTMLButtonElement => { + const getConditionBuilderAndButton = (dataTestSub: string = formPrefix): HTMLButtonElement => { return renderResult.getByTestId( `${dataTestSub}-conditionsBuilder-group1-AndButton` ) as HTMLButtonElement; }; - const getConditionBuilderAndConnectorBadge = ( - dataTestSub: string = dataTestSubjForForm - ): HTMLElement => { + const getConditionBuilderAndConnectorBadge = (dataTestSub: string = formPrefix): HTMLElement => { return renderResult.getByTestId(`${dataTestSub}-conditionsBuilder-group1-andConnector`); }; const getAllValidationErrors = (): HTMLElement[] => { @@ -121,42 +176,44 @@ describe('When using the Trusted App Form', () => { beforeEach(() => { resetHTMLElementOffsetWidth = forceHTMLElementOffsetWidth(); - useIsExperimentalFeatureEnabledMock.mockReturnValue(true); (licenseService.isPlatinumPlus as jest.Mock).mockReturnValue(true); - mockedContext = createAppRootMockRenderer(); - - latestUpdatedTrustedApp = defaultNewTrustedApp(); + latestUpdatedItem = createItem(); formProps = { - 'data-test-subj': dataTestSubjForForm, - trustedApp: latestUpdatedTrustedApp, - isEditMode: false, - isDirty: false, - wasByPolicy: false, + item: latestUpdatedItem, + mode: 'create', + disabled: false, + error: undefined, + policiesIsLoading: false, onChange: jest.fn((updates) => { - latestUpdatedTrustedApp = updates.item; + latestUpdatedItem = updates.item; }), - policies: { - options: [], - }, + policies: [], }; }); afterEach(() => { resetHTMLElementOffsetWidth(); - reactTestingLibrary.cleanup(); + cleanup(); }); - describe('and the form is rendered', () => { + describe('Details and Conditions', () => { beforeEach(() => render()); - it('should show Name as required after blur', () => { - expect(getNameField().required).toBe(false); - reactTestingLibrary.act(() => { - fireEvent.blur(getNameField()); - }); - expect(getNameField().required).toBe(true); + it('should NOT initially show any inline validation errors', () => { + expect(renderResult.container.querySelectorAll('.euiFormErrorText').length).toBe(0); + }); + + it('should hide details text when in edit mode', () => { + formProps.mode = 'edit'; + rerenderWithLatestProps(); + expect(getDetailsBlurb()).toBeNull(); + }); + + it('should show name required name blur', () => { + setTextFieldValue(getNameField(), ' '); + expect(renderResult.getByText(INPUT_ERRORS.name)).toBeTruthy(); }); it('should default OS to Windows', () => { @@ -165,42 +222,68 @@ describe('When using the Trusted App Form', () => { it('should allow user to select between 3 OSs', () => { const osField = getOsField(); - reactTestingLibrary.act(() => { - fireEvent.click(osField, { button: 1 }); - }); + userEvent.click(osField, { button: 1 }); const options = Array.from( renderResult.baseElement.querySelectorAll( '.euiSuperSelect__listbox button.euiSuperSelect__item' ) ).map((button) => button.textContent); - expect(options).toEqual(['Mac', 'Windows', 'Linux']); + expect(options).toEqual(['Linux', 'Mac', 'Windows']); }); it('should show Description as optional', () => { expect(getDescriptionField().required).toBe(false); }); - it('should NOT initially show any inline validation errors', () => { - expect(renderResult.container.querySelectorAll('.euiFormErrorText').length).toBe(0); + it('should be invalid if no name', () => { + const emptyName = ' '; + setTextFieldValue(getNameField(), emptyName); + const expected = createOnChangeArgs({ + item: createItem({ + name: emptyName, + }), + }); + expect(formProps.onChange).toHaveBeenCalledWith(expected); }); - it('should show top-level Errors', () => { - formProps.isInvalid = true; - formProps.error = 'a top level error'; - rerender(); - expect(renderResult.queryByText(formProps.error as string)).not.toBeNull(); + it('should correctly edit name', () => { + setTextFieldValue(getNameField(), 'z'); + const expected = createOnChangeArgs({ + item: createItem({ + name: 'z', + }), + }); + expect(formProps.onChange).toHaveBeenCalledWith(expected); + }); + + it('should correctly edit description', () => { + setTextFieldValue(getDescriptionField(), 'describe ta'); + const expected = createOnChangeArgs({ + item: createItem({ description: 'describe ta' }), + }); + expect(formProps.onChange).toHaveBeenCalledWith(expected); + }); + + it('should correctly change OS', () => { + userEvent.click(getOsField()); + userEvent.click(screen.getByRole('option', { name: 'Linux' })); + const expected = createOnChangeArgs({ + item: createItem({ os_types: [OperatingSystem.LINUX] }), + }); + expect(formProps.onChange).toHaveBeenCalledWith(expected); }); }); - describe('the condition builder component', () => { + describe('ConditionBuilder', () => { beforeEach(() => render()); - it('should show an initial condition entry with labels', () => { + it('should default to hash entry field', () => { const defaultCondition = getCondition(); const labels = Array.from(defaultCondition.querySelectorAll('.euiFormRow__labelWrapper')).map( (label) => (label.textContent || '').trim() ); expect(labels).toEqual(['Field', 'Operator', 'Value', '']); + expect(formProps.onChange).not.toHaveBeenCalled(); }); it('should not allow the entry to be removed if its the only one displayed', () => { @@ -210,9 +293,7 @@ describe('When using the Trusted App Form', () => { it('should display 3 options for Field for Windows', () => { const conditionFieldSelect = getConditionFieldSelect(getCondition()); - reactTestingLibrary.act(() => { - fireEvent.click(conditionFieldSelect, { button: 1 }); - }); + userEvent.click(conditionFieldSelect, { button: 1 }); const options = Array.from( renderResult.baseElement.querySelectorAll( '.euiSuperSelect__listbox button.euiSuperSelect__item' @@ -228,12 +309,37 @@ describe('When using the Trusted App Form', () => { it('should show the value field as required after blur', () => { expect(getConditionValue(getCondition()).required).toEqual(false); - reactTestingLibrary.act(() => { + act(() => { fireEvent.blur(getConditionValue(getCondition())); }); expect(getConditionValue(getCondition()).required).toEqual(true); }); + it('should show path malformed warning', () => { + render(); + expect(screen.queryByText(INPUT_ERRORS.pathWarning(0))).toBeNull(); + + const propsItem: Partial = { + entries: [createEntry(ConditionEntryField.PATH, 'match', 'malformed-path')], + }; + formProps.item = { ...formProps.item, ...propsItem }; + render(); + expect(screen.getByText(INPUT_ERRORS.pathWarning(0))).not.toBeNull(); + }); + + it('should show wildcard in path warning', () => { + render(); + expect(screen.queryByText(INPUT_ERRORS.wildcardPathWarning(0))).toBeNull(); + + const propsItem: Partial = { + os_types: [OperatingSystem.LINUX], + entries: [createEntry(ConditionEntryField.PATH, 'wildcard', '/sys/wil*/*.app')], + }; + formProps.item = { ...formProps.item, ...propsItem }; + render(); + expect(screen.getByText(INPUT_ERRORS.wildcardPathWarning(0))).not.toBeNull(); + }); + it('should display the `AND` button', () => { const andButton = getConditionBuilderAndButton(); expect(andButton.textContent).toEqual('AND'); @@ -243,11 +349,9 @@ describe('When using the Trusted App Form', () => { describe('and when the AND button is clicked', () => { beforeEach(() => { const andButton = getConditionBuilderAndButton(); - reactTestingLibrary.act(() => { - fireEvent.click(andButton, { button: 1 }); - }); + userEvent.click(andButton, { button: 1 }); // re-render with updated `newTrustedApp` - formProps.trustedApp = formProps.onChange.mock.calls[0][0].item; + formProps.item = formProps.onChange.mock.calls[0][0].item; rerender(); }); @@ -270,127 +374,105 @@ describe('When using the Trusted App Form', () => { describe('the Policy Selection area', () => { beforeEach(() => { - const policy = generator.generatePolicyPackagePolicy(); - policy.name = 'test policy A'; - policy.id = '123'; - - formProps.policies.options = [policy]; + formProps.policies = createPolicies(); }); it('should have `global` switch on if effective scope is global and policy options hidden', () => { render(); const globalButton = renderResult.getByTestId( - `${dataTestSubjForForm}-effectedPolicies-global` + `${formPrefix}-effectedPolicies-global` ) as HTMLButtonElement; expect(globalButton.classList.contains('euiButtonGroupButton-isSelected')).toEqual(true); - expect(renderResult.queryByTestId('policy-123')).toBeNull(); + expect( + renderResult.queryByTestId(`${formPrefix}-effectedPolicies-policiesSelectable`) + ).toBeNull(); + expect(renderResult.queryByTestId('policy-id-0')).toBeNull(); }); it('should have policy options visible and specific policies checked if scope is per-policy', () => { - (formProps.trustedApp as NewTrustedApp).effectScope = { - type: 'policy', - policies: ['123'], - }; + formProps.item.tags = [formProps.policies.map((p) => `policy:${p.id}`)[0]]; render(); + const perPolicyButton = renderResult.getByTestId( - `${dataTestSubjForForm}-effectedPolicies-perPolicy` + `${formPrefix}-effectedPolicies-perPolicy` ) as HTMLButtonElement; expect(perPolicyButton.classList.contains('euiButtonGroupButton-isSelected')).toEqual(true); - expect(renderResult.getByTestId('policy-123').getAttribute('aria-disabled')).toEqual('false'); - expect(renderResult.getByTestId('policy-123').getAttribute('aria-checked')).toEqual('true'); + expect(renderResult.getByTestId('policy-id-0').getAttribute('aria-disabled')).toEqual( + 'false' + ); + expect(renderResult.getByTestId('policy-id-0-checkbox')).toBeChecked(); }); + it('should show loader when setting `policies.isLoading` to true and scope is per-policy', () => { - formProps.policies.isLoading = true; - (formProps.trustedApp as NewTrustedApp).effectScope = { - type: 'policy', - policies: ['123'], - }; + formProps.policiesIsLoading = true; + formProps.item.tags = [formProps.policies.map((p) => `policy:${p.id}`)[0]]; render(); expect(renderResult.queryByTestId('loading-spinner')).not.toBeNull(); }); }); - describe('the Policy Selection area under feature flag', () => { - it("shouldn't display the policiy selection area ", () => { - useIsExperimentalFeatureEnabledMock.mockReturnValue(false); - render(); - expect( - renderResult.queryByText('Apply trusted application globally') - ).not.toBeInTheDocument(); - }); - }); - describe('the Policy Selection area when the license downgrades to gold or below', () => { beforeEach(() => { - // select per policy for trusted app - const policy = generator.generatePolicyPackagePolicy(); - policy.name = 'test policy A'; - policy.id = '123'; - - formProps.policies.options = [policy]; - - (formProps.trustedApp as NewTrustedApp).effectScope = { - type: 'policy', - policies: ['123'], - }; - - formProps.isEditMode = true; - + const policies = createPolicies(); + formProps.policies = policies; + formProps.item.tags = [policies.map((p) => `policy:${p.id}`)[0]]; + formProps.mode = 'edit'; // downgrade license (licenseService.isPlatinumPlus as jest.Mock).mockReturnValue(false); + render(); }); it('maintains policy configuration but does not allow the user to edit add/remove individual policies in edit mode', () => { - render(); const perPolicyButton = renderResult.getByTestId( - `${dataTestSubjForForm}-effectedPolicies-perPolicy` + `${formPrefix}-effectedPolicies-perPolicy` ) as HTMLButtonElement; - expect(perPolicyButton.classList.contains('euiButtonGroupButton-isSelected')).toEqual(true); - expect(renderResult.getByTestId('policy-123').getAttribute('aria-disabled')).toEqual('true'); - expect(renderResult.getByTestId('policy-123-checkbox')).toBeChecked(); + expect(renderResult.getByTestId('policy-id-0').getAttribute('aria-disabled')).toEqual('true'); + expect(renderResult.getByTestId('policy-id-0-checkbox')).toBeChecked(); }); it("allows the user to set the trusted app entry to 'Global' in the edit option", () => { - render(); const globalButtonInput = renderResult.getByTestId('globalPolicy') as HTMLButtonElement; - - reactTestingLibrary.act(() => { + act(() => { fireEvent.click(globalButtonInput); }); - expect(formProps.onChange.mock.calls[0][0].item.effectScope.type).toBe('global'); + const policyItem = formProps.onChange.mock.calls[0][0].item.tags + ? formProps.onChange.mock.calls[0][0].item.tags[0] + : ''; + expect(policyItem).toBe('policy:all'); }); + it('hides the policy assignment section if the TA is set to global', () => { - (formProps.trustedApp as NewTrustedApp).effectScope = { - type: 'global', - }; - expect(renderResult.queryByTestId(`${dataTestSubjForForm}-effectedPolicies`)).toBeNull(); + formProps.item.tags = ['policy:all']; + rerender(); + expect(renderResult.queryByTestId(`${formPrefix}-effectedPolicies`)).toBeNull(); }); it('hides the policy assignment section if the user is adding a new TA', () => { - formProps.isEditMode = false; - expect(renderResult.queryByTestId(`${dataTestSubjForForm}-effectedPolicies`)).toBeNull(); + formProps.mode = 'create'; + rerender(); + expect(renderResult.queryByTestId(`${formPrefix}-effectedPolicies`)).toBeNull(); }); }); describe('and the user visits required fields but does not fill them out', () => { beforeEach(() => { render(); - reactTestingLibrary.act(() => { + act(() => { fireEvent.blur(getNameField()); }); - reactTestingLibrary.act(() => { + act(() => { fireEvent.blur(getConditionValue(getCondition())); }); }); it('should show Name validation error', () => { - expect(renderResult.getByText('Name is required')); + expect(renderResult.getByText(INPUT_ERRORS.name)).not.toBeNull(); }); it('should show Condition validation error', () => { - expect(renderResult.getByText('[1] Field entry must have a value')); + expect(renderResult.getByText(INPUT_ERRORS.mustHaveValue(0))); }); it('should NOT display any other errors', () => { @@ -403,129 +485,107 @@ describe('When using the Trusted App Form', () => { it('should validate that Name has a non empty space value', () => { setTextFieldValue(getNameField(), ' '); - expect(renderResult.getByText('Name is required')); + expect(renderResult.getByText(INPUT_ERRORS.name)); }); it('should validate invalid Hash value', () => { setTextFieldValue(getConditionValue(getCondition()), 'someHASH'); - expect(renderResult.getByText('[1] Invalid hash value')); + expect(renderResult.getByText(INPUT_ERRORS.invalidHash(0))); }); it('should validate that a condition value has a non empty space value', () => { setTextFieldValue(getConditionValue(getCondition()), ' '); - expect(renderResult.getByText('[1] Field entry must have a value')); + expect(renderResult.getByText(INPUT_ERRORS.mustHaveValue(0))); }); it('should validate all condition values (when multiples exist) have non empty space value', () => { const andButton = getConditionBuilderAndButton(); - reactTestingLibrary.act(() => { - fireEvent.click(andButton, { button: 1 }); - }); - rerenderWithLatestTrustedApp(); + userEvent.click(andButton, { button: 1 }); + rerenderWithLatestProps(); setTextFieldValue(getConditionValue(getCondition()), 'someHASH'); - rerenderWithLatestTrustedApp(); + rerenderWithLatestProps(); - expect(renderResult.getByText('[2] Field entry must have a value')); + expect(renderResult.getByText(INPUT_ERRORS.mustHaveValue(1))); }); it('should validate duplicated conditions', () => { const andButton = getConditionBuilderAndButton(); - reactTestingLibrary.act(() => { - fireEvent.click(andButton, { button: 1 }); - }); + userEvent.click(andButton, { button: 1 }); setTextFieldValue(getConditionValue(getCondition()), ''); - rerenderWithLatestTrustedApp(); + rerenderWithLatestProps(); - expect(renderResult.getByText('Hash cannot be added more than once')); + expect(renderResult.getByText(INPUT_ERRORS.noDuplicateField(ConditionEntryField.HASH))); }); it('should validate multiple errors in form', () => { const andButton = getConditionBuilderAndButton(); - reactTestingLibrary.act(() => { - fireEvent.click(andButton, { button: 1 }); - }); - rerenderWithLatestTrustedApp(); + userEvent.click(andButton, { button: 1 }); + rerenderWithLatestProps(); setTextFieldValue(getConditionValue(getCondition()), 'someHASH'); - rerenderWithLatestTrustedApp(); - expect(renderResult.getByText('[1] Invalid hash value')); - expect(renderResult.getByText('[2] Field entry must have a value')); + rerenderWithLatestProps(); + expect(renderResult.getByText(INPUT_ERRORS.invalidHash(0))); + expect(renderResult.getByText(INPUT_ERRORS.mustHaveValue(1))); }); }); describe('and all required data passes validation', () => { it('should call change callback with isValid set to true and contain the new item', () => { + const propsItem: Partial = { + os_types: [OperatingSystem.LINUX], + name: 'Some process', + description: 'some description', + entries: [ + createEntry(ConditionEntryField.HASH, 'match', 'e50fb1a0e5fff590ece385082edc6c41'), + ], + }; + formProps.item = { ...formProps.item, ...propsItem }; render(); - - setTextFieldValue(getNameField(), 'Some Process'); - rerenderWithLatestTrustedApp(); - - setTextFieldValue(getConditionValue(getCondition()), 'e50fb1a0e5fff590ece385082edc6c41'); - rerenderWithLatestTrustedApp(); - - setTextFieldValue(getDescriptionField(), 'some description'); - rerenderWithLatestTrustedApp(); + act(() => { + fireEvent.blur(getNameField()); + }); expect(getAllValidationErrors()).toHaveLength(0); - expect(formProps.onChange).toHaveBeenLastCalledWith({ + const expected = createOnChangeArgs({ isValid: true, - item: { - name: 'Some Process', - description: 'some description', - os: OperatingSystem.WINDOWS, - effectScope: { type: 'global' }, - entries: [ - { - field: ConditionEntryField.HASH, - operator: 'included', - type: 'match', - value: 'e50fb1a0e5fff590ece385082edc6c41', - }, - ], - }, + item: createItem(propsItem), }); + expect(formProps.onChange).toHaveBeenCalledWith(expected); }); it('should not validate form to true if name input is empty', () => { - const props = { + const propsItem: Partial = { name: 'some name', - description: '', - effectScope: { - type: 'global', - }, - os: OperatingSystem.WINDOWS, - entries: [ - { field: ConditionEntryField.PATH, operator: 'included', type: 'wildcard', value: 'x' }, - ], - } as NewTrustedApp; - - formProps.trustedApp = props; + os_types: [OperatingSystem.LINUX], + entries: [createEntry(ConditionEntryField.PATH, 'wildcard', '/sys/usr*/doc.app')], + }; + formProps.item = { ...formProps.item, ...propsItem }; render(); + expect(getAllValidationErrors()).toHaveLength(0); + expect(getAllValidationWarnings()).toHaveLength(0); - formProps.trustedApp = { - ...props, - name: '', - }; + formProps.item.name = ''; rerender(); - - expect(getAllValidationErrors()).toHaveLength(0); - expect(getAllValidationWarnings()).toHaveLength(1); + act(() => { + fireEvent.blur(getNameField()); + }); + expect(getAllValidationErrors()).toHaveLength(1); + expect(getAllValidationWarnings()).toHaveLength(0); expect(formProps.onChange).toHaveBeenLastCalledWith({ isValid: false, item: { + ...formProps.item, name: '', - description: '', - os: OperatingSystem.WINDOWS, - effectScope: { type: 'global' }, + os_types: [OperatingSystem.LINUX], entries: [ { field: ConditionEntryField.PATH, operator: 'included', type: 'wildcard', - value: 'x', + value: '/sys/usr*/doc.app', }, ], }, diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/form.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/form.tsx new file mode 100644 index 000000000000..9656b5821b75 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/form.tsx @@ -0,0 +1,519 @@ +/* + * 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 React, { ChangeEventHandler, memo, useCallback, useEffect, useMemo, useState } from 'react'; +import { + EuiFieldText, + EuiForm, + EuiFormRow, + EuiHorizontalRule, + EuiSuperSelect, + EuiSuperSelectOption, + EuiTextArea, + EuiText, + EuiTitle, + EuiSpacer, +} from '@elastic/eui'; +import { + hasSimpleExecutableName, + isPathValid, + ConditionEntryField, + OperatingSystem, + AllConditionEntryFields, + EntryTypes, +} from '@kbn/securitysolution-utils'; +import { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; + +import { TrustedAppConditionEntry, NewTrustedApp } from '../../../../../../common/endpoint/types'; +import { + isValidHash, + getDuplicateFields, +} from '../../../../../../common/endpoint/service/artifacts/validations'; + +import { + isArtifactGlobal, + getPolicyIdsFromArtifact, +} from '../../../../../../common/endpoint/service/artifacts'; +import { + isMacosLinuxTrustedAppCondition, + isWindowsTrustedAppCondition, +} from '../../state/type_guards'; + +import { + CONDITIONS_HEADER, + CONDITIONS_HEADER_DESCRIPTION, + DETAILS_HEADER, + DETAILS_HEADER_DESCRIPTION, + DESCRIPTION_LABEL, + INPUT_ERRORS, + NAME_LABEL, + POLICY_SELECT_DESCRIPTION, + SELECT_OS_LABEL, +} from '../translations'; +import { OS_TITLES } from '../../../../common/translations'; +import { LogicalConditionBuilder, LogicalConditionBuilderProps } from './logical_condition'; +import { useTestIdGenerator } from '../../../../components/hooks/use_test_id_generator'; +import { useLicense } from '../../../../../common/hooks/use_license'; +import { + EffectedPolicySelect, + EffectedPolicySelection, +} from '../../../../components/effected_policy_select'; +import { + GLOBAL_ARTIFACT_TAG, + BY_POLICY_ARTIFACT_TAG_PREFIX, +} from '../../../../../../common/endpoint/service/artifacts/constants'; +import type { PolicyData } from '../../../../../../common/endpoint/types'; +import { ArtifactFormComponentProps } from '../../../../components/artifact_list_page'; +import { isGlobalPolicyEffected } from '../../../../components/effected_policy_select/utils'; + +interface FieldValidationState { + /** If this fields state is invalid. Drives display of errors on the UI */ + isInvalid: boolean; + errors: React.ReactNode[]; + warnings: React.ReactNode[]; +} +interface ValidationResult { + /** Overall indicator if form is valid */ + isValid: boolean; + + /** Individual form field validations */ + result: Partial<{ + [key in keyof NewTrustedApp]: FieldValidationState; + }>; +} + +const addResultToValidation = ( + validation: ValidationResult, + field: keyof NewTrustedApp, + type: 'warnings' | 'errors', + resultValue: React.ReactNode +) => { + if (!validation.result[field]) { + validation.result[field] = { + isInvalid: false, + errors: [], + warnings: [], + }; + } + const errorMarkup: React.ReactNode = type === 'warnings' ?
{resultValue}
: resultValue; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + validation.result[field]![type].push(errorMarkup); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + validation.result[field]!.isInvalid = true; +}; + +const validateValues = (values: ArtifactFormComponentProps['item']): ValidationResult => { + let isValid: ValidationResult['isValid'] = true; + const validation: ValidationResult = { + isValid, + result: {}, + }; + + // Name field + if (!values.name.trim()) { + isValid = false; + addResultToValidation(validation, 'name', 'errors', INPUT_ERRORS.name); + } + + if (!values.os_types) { + isValid = false; + addResultToValidation(validation, 'os', 'errors', INPUT_ERRORS.os); + } + + const os = ((values.os_types ?? [])[0] as OperatingSystem) ?? OperatingSystem.WINDOWS; + if (!values.entries.length) { + isValid = false; + addResultToValidation(validation, 'entries', 'errors', INPUT_ERRORS.field); + } else { + const duplicated = getDuplicateFields(values.entries as TrustedAppConditionEntry[]); + if (duplicated.length) { + isValid = false; + duplicated.forEach((field: ConditionEntryField) => { + addResultToValidation( + validation, + 'entries', + 'errors', + INPUT_ERRORS.noDuplicateField(field) + ); + }); + } + values.entries.forEach((entry, index) => { + const isValidPathEntry = isPathValid({ + os, + field: entry.field as AllConditionEntryFields, + type: entry.type as EntryTypes, + value: (entry as TrustedAppConditionEntry).value, + }); + + if (!entry.field || !(entry as TrustedAppConditionEntry).value.trim()) { + isValid = false; + addResultToValidation(validation, 'entries', 'errors', INPUT_ERRORS.mustHaveValue(index)); + } else if ( + entry.field === ConditionEntryField.HASH && + !isValidHash((entry as TrustedAppConditionEntry).value) + ) { + isValid = false; + addResultToValidation(validation, 'entries', 'errors', INPUT_ERRORS.invalidHash(index)); + } else if (!isValidPathEntry) { + addResultToValidation(validation, 'entries', 'warnings', INPUT_ERRORS.pathWarning(index)); + } else if ( + isValidPathEntry && + !hasSimpleExecutableName({ + os, + value: (entry as TrustedAppConditionEntry).value, + type: entry.type as EntryTypes, + }) + ) { + addResultToValidation( + validation, + 'entries', + 'warnings', + INPUT_ERRORS.wildcardPathWarning(index) + ); + } + }); + } + + validation.isValid = isValid; + return validation; +}; + +const defaultConditionEntry = (): TrustedAppConditionEntry => ({ + field: ConditionEntryField.HASH, + operator: 'included', + type: 'match', + value: '', +}); + +export const TrustedAppsForm = memo( + ({ item, policies, policiesIsLoading, onChange, mode }) => { + const getTestId = useTestIdGenerator('trustedApps-form'); + const [visited, setVisited] = useState< + Partial<{ + [key in keyof NewTrustedApp]: boolean; + }> + >({}); + + const [selectedPolicies, setSelectedPolicies] = useState([]); + const isPlatinumPlus = useLicense().isPlatinumPlus(); + const isGlobal = useMemo(() => isArtifactGlobal(item as ExceptionListItemSchema), [item]); + const [wasByPolicy, setWasByPolicy] = useState(!isGlobalPolicyEffected(item.tags)); + const [hasFormChanged, setHasFormChanged] = useState(false); + + useEffect(() => { + if (!hasFormChanged && item.tags) { + setWasByPolicy(!isGlobalPolicyEffected(item.tags)); + } + }, [item.tags, hasFormChanged]); + + // select policies if editing + useEffect(() => { + if (hasFormChanged) { + return; + } + const policyIds = item.tags ? getPolicyIdsFromArtifact({ tags: item.tags }) : []; + if (!policyIds.length) { + return; + } + const policiesData = policies.filter((policy) => policyIds.includes(policy.id)); + + setSelectedPolicies(policiesData); + }, [hasFormChanged, item, policies]); + + const showAssignmentSection = useMemo(() => { + return ( + isPlatinumPlus || + (mode === 'edit' && (!isGlobal || (wasByPolicy && isGlobal && hasFormChanged))) + ); + }, [mode, isGlobal, hasFormChanged, isPlatinumPlus, wasByPolicy]); + + const [validationResult, setValidationResult] = useState(() => + validateValues(item) + ); + + const processChanged = useCallback( + (updatedFormValues: ArtifactFormComponentProps['item']) => { + const updatedValidationResult = validateValues(updatedFormValues); + setValidationResult(updatedValidationResult); + onChange({ + item: updatedFormValues, + isValid: updatedValidationResult.isValid, + }); + }, + [onChange] + ); + + const handleOnPolicyChange = useCallback( + (change: EffectedPolicySelection) => { + const tags = change.isGlobal + ? [GLOBAL_ARTIFACT_TAG] + : change.selected.map((policy) => `${BY_POLICY_ARTIFACT_TAG_PREFIX}${policy.id}`); + + const nextItem = { ...item, tags }; + // Preserve old selected policies when switching to global + if (!change.isGlobal) { + setSelectedPolicies(change.selected); + } + processChanged(nextItem); + setHasFormChanged(true); + }, + [item, processChanged] + ); + + const handleOnNameOrDescriptionChange = useCallback< + ChangeEventHandler + >( + (event: React.ChangeEvent) => { + const nextItem = { + ...item, + [event.target.name]: event.target.value, + }; + + processChanged(nextItem); + setHasFormChanged(true); + }, + [item, processChanged] + ); + + const handleOnNameBlur = useCallback( + ({ target: { name } }) => { + processChanged(item); + setVisited((prevVisited) => ({ ...prevVisited, [name]: true })); + }, + [item, processChanged] + ); + + const osOptions: Array> = useMemo( + () => + [OperatingSystem.LINUX, OperatingSystem.MAC, OperatingSystem.WINDOWS].map((os) => ({ + value: os, + inputDisplay: OS_TITLES[os], + })), + [] + ); + + const handleOnOsChange = useCallback( + (os: OperatingSystem) => { + setVisited((prevVisited) => { + return { + ...prevVisited, + os: true, + }; + }); + + const nextItem: ArtifactFormComponentProps['item'] = { + ...item, + os_types: [os], + entries: [] as ArtifactFormComponentProps['item']['entries'], + }; + + if (os !== OperatingSystem.WINDOWS) { + const macOsLinuxConditionEntry = item.entries.filter((entry) => + isMacosLinuxTrustedAppCondition(entry as TrustedAppConditionEntry) + ); + nextItem.entries.push(...macOsLinuxConditionEntry); + if (item.entries.length === 0) { + nextItem.entries.push(defaultConditionEntry()); + } + } else { + nextItem.entries.push(...item.entries); + } + + processChanged(nextItem); + setHasFormChanged(true); + }, + [item, processChanged] + ); + + const handleConditionBuilderOnVisited: LogicalConditionBuilderProps['onVisited'] = + useCallback(() => { + setVisited((prevState) => { + return { + ...prevState, + entries: true, + }; + }); + }, []); + + const handleEntryChange = useCallback( + (newEntry, oldEntry) => { + const nextItem: ArtifactFormComponentProps['item'] = { + ...item, + entries: item.entries.map((e) => { + if (e === oldEntry) { + return newEntry; + } + return e; + }), + }; + + processChanged(nextItem); + setHasFormChanged(true); + }, + [item, processChanged] + ); + + const handleEntryRemove = useCallback( + (entry: NewTrustedApp['entries'][0]) => { + const nextItem: ArtifactFormComponentProps['item'] = { + ...item, + entries: item.entries.filter((e) => e !== entry), + }; + + processChanged(nextItem); + setHasFormChanged(true); + }, + [item, processChanged] + ); + + const handleAndClick = useCallback(() => { + const nextItem: ArtifactFormComponentProps['item'] = { + ...item, + entries: [], + }; + const os = ((item.os_types ?? [])[0] as OperatingSystem) ?? OperatingSystem.WINDOWS; + if (os === OperatingSystem.WINDOWS) { + nextItem.entries = [...item.entries, defaultConditionEntry()].filter((entry) => + isWindowsTrustedAppCondition(entry as TrustedAppConditionEntry) + ); + } else { + nextItem.entries = [ + ...item.entries.filter((entry) => + isMacosLinuxTrustedAppCondition(entry as TrustedAppConditionEntry) + ), + defaultConditionEntry(), + ]; + } + processChanged(nextItem); + setHasFormChanged(true); + }, [item, processChanged]); + + const selectedOs = useMemo((): OperatingSystem => { + if (!item?.os_types?.length) { + return OperatingSystem.WINDOWS; + } + return item.os_types[0] as OperatingSystem; + }, [item?.os_types]); + + const trustedApp = useMemo(() => { + const ta = item; + + ta.entries = item.entries.length + ? (item.entries as TrustedAppConditionEntry[]) + : [defaultConditionEntry()]; + + return ta; + }, [item]); + + return ( + + +

{DETAILS_HEADER}

+
+ + {mode === 'create' && ( + +

{DETAILS_HEADER_DESCRIPTION}

+
+ )} + + + + + + + + + +

{CONDITIONS_HEADER}

+
+ + {CONDITIONS_HEADER_DESCRIPTION} + + + + + + + + {showAssignmentSection ? ( + <> + + + + + + ) : null} +
+ ); + } +); + +TrustedAppsForm.displayName = 'TrustedAppsForm'; diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_grid/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_grid/__snapshots__/index.test.tsx.snap deleted file mode 100644 index 8b496dcb519b..000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_grid/__snapshots__/index.test.tsx.snap +++ /dev/null @@ -1,12717 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`TrustedAppsGrid renders correctly initially 1`] = ` -.c1 { - position: relative; - padding-top: 4px; -} - -.c1 .body { - min-height: 40px; -} - -.c1 .body-content { - position: relative; -} - -.c0 .trusted-app + .trusted-app { - margin-top: 24px; -} - -
-
-
-
-
-
-
-
-
- - No items found - -
-
-
-
-
-
-
-
-
-`; - -exports[`TrustedAppsGrid renders correctly when failed loading data for the first time 1`] = ` -.c1 { - position: relative; - padding-top: 4px; -} - -.c1 .body { - min-height: 40px; -} - -.c1 .body-content { - position: relative; -} - -.c0 .trusted-app + .trusted-app { - margin-top: 24px; -} - -
-
-
-
-
-
-
-
- - - Intenal Server Error -
-
-
-
-
-
-
-
-`; - -exports[`TrustedAppsGrid renders correctly when failed loading data for the second time 1`] = ` -.c1 { - position: relative; - padding-top: 4px; -} - -.c1 .body { - min-height: 40px; -} - -.c1 .body-content { - position: relative; -} - -.c0 .trusted-app + .trusted-app { - margin-top: 24px; -} - -
-
-
-
-
-
-
-
- - - Intenal Server Error -
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
- -
-
-
-
-
-
-`; - -exports[`TrustedAppsGrid renders correctly when loaded data 1`] = ` -.c1 { - position: relative; - padding-top: 4px; -} - -.c1 .body { - min-height: 40px; -} - -.c1 .body-content { - position: relative; -} - -.c6 { - padding-top: 2px; -} - -.c5 { - margin-bottom: 4px !important; -} - -.c7 { - margin: 6px; -} - -.c3.artifactEntryCard + .c2.artifactEntryCard { - margin-top: 24px; -} - -.c4 { - padding: 32px; -} - -.c4.top-section { - padding-bottom: 24px; -} - -.c4.bottom-section { - padding-top: 24px; -} - -.c4.artifact-entry-collapsible-card { - padding: 24px !important; -} - -.c0 .trusted-app + .trusted-app { - margin-top: 24px; -} - -
-
-
-
-
-
-
-
-
-
-
-

- trusted app 0 -

-
-
-
-
-
-
- -
-
-
-
-
- Last updated -
-
-
-
- - 1 minute ago - -
-
-
-
-
-
-
-
-
- -
-
-
-
-
- Created -
-
-
-
- - 1 minute ago - -
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
- - - - Created by - - - -
-
-
-
- -
-
-
- someone -
-
-
-
-
-
-
-
-
- - - - Updated by - - - -
-
-
-
- -
-
-
- someone -
-
-
-
-
-
-
-
-
-
-
- -
-
-
- Applied globally -
-
-
-
-
-
-
- Trusted App 0 -
-
-
-
-
-
- - - - - - OS - - - - - IS - - - - Windows - - - -
-
-
-
-
-
-
-
-
-
-

- trusted app 1 -

-
-
-
-
-
-
- -
-
-
-
-
- Last updated -
-
-
-
- - 1 minute ago - -
-
-
-
-
-
-
-
-
- -
-
-
-
-
- Created -
-
-
-
- - 1 minute ago - -
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
- - - - Created by - - - -
-
-
-
- -
-
-
- someone -
-
-
-
-
-
-
-
-
- - - - Updated by - - - -
-
-
-
- -
-
-
- someone -
-
-
-
-
-
-
-
-
-
-
- -
-
-
- Applied globally -
-
-
-
-
-
-
- Trusted App 1 -
-
-
-
-
-
- - - - - - OS - - - - - IS - - - - Mac - - - -
-
-
-
-
-
-
-
-
-
-

- trusted app 2 -

-
-
-
-
-
-
- -
-
-
-
-
- Last updated -
-
-
-
- - 1 minute ago - -
-
-
-
-
-
-
-
-
- -
-
-
-
-
- Created -
-
-
-
- - 1 minute ago - -
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
- - - - Created by - - - -
-
-
-
- -
-
-
- someone -
-
-
-
-
-
-
-
-
- - - - Updated by - - - -
-
-
-
- -
-
-
- someone -
-
-
-
-
-
-
-
-
-
-
- -
-
-
- Applied globally -
-
-
-
-
-
-
- Trusted App 2 -
-
-
-
-
-
- - - - - - OS - - - - - IS - - - - Linux - - - -
-
-
-
-
-
-
-
-
-
-

- trusted app 3 -

-
-
-
-
-
-
- -
-
-
-
-
- Last updated -
-
-
-
- - 1 minute ago - -
-
-
-
-
-
-
-
-
- -
-
-
-
-
- Created -
-
-
-
- - 1 minute ago - -
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
- - - - Created by - - - -
-
-
-
- -
-
-
- someone -
-
-
-
-
-
-
-
-
- - - - Updated by - - - -
-
-
-
- -
-
-
- someone -
-
-
-
-
-
-
-
-
-
-
- -
-
-
- Applied globally -
-
-
-
-
-
-
- Trusted App 3 -
-
-
-
-
-
- - - - - - OS - - - - - IS - - - - Windows - - - -
-
-
-
-
-
-
-
-
-
-

- trusted app 4 -

-
-
-
-
-
-
- -
-
-
-
-
- Last updated -
-
-
-
- - 1 minute ago - -
-
-
-
-
-
-
-
-
- -
-
-
-
-
- Created -
-
-
-
- - 1 minute ago - -
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
- - - - Created by - - - -
-
-
-
- -
-
-
- someone -
-
-
-
-
-
-
-
-
- - - - Updated by - - - -
-
-
-
- -
-
-
- someone -
-
-
-
-
-
-
-
-
-
-
- -
-
-
- Applied globally -
-
-
-
-
-
-
- Trusted App 4 -
-
-
-
-
-
- - - - - - OS - - - - - IS - - - - Mac - - - -
-
-
-
-
-
-
-
-
-
-

- trusted app 5 -

-
-
-
-
-
-
- -
-
-
-
-
- Last updated -
-
-
-
- - 1 minute ago - -
-
-
-
-
-
-
-
-
- -
-
-
-
-
- Created -
-
-
-
- - 1 minute ago - -
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
- - - - Created by - - - -
-
-
-
- -
-
-
- someone -
-
-
-
-
-
-
-
-
- - - - Updated by - - - -
-
-
-
- -
-
-
- someone -
-
-
-
-
-
-
-
-
-
-
- -
-
-
- Applied globally -
-
-
-
-
-
-
- Trusted App 5 -
-
-
-
-
-
- - - - - - OS - - - - - IS - - - - Linux - - - -
-
-
-
-
-
-
-
-
-
-

- trusted app 6 -

-
-
-
-
-
-
- -
-
-
-
-
- Last updated -
-
-
-
- - 1 minute ago - -
-
-
-
-
-
-
-
-
- -
-
-
-
-
- Created -
-
-
-
- - 1 minute ago - -
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
- - - - Created by - - - -
-
-
-
- -
-
-
- someone -
-
-
-
-
-
-
-
-
- - - - Updated by - - - -
-
-
-
- -
-
-
- someone -
-
-
-
-
-
-
-
-
-
-
- -
-
-
- Applied globally -
-
-
-
-
-
-
- Trusted App 6 -
-
-
-
-
-
- - - - - - OS - - - - - IS - - - - Windows - - - -
-
-
-
-
-
-
-
-
-
-

- trusted app 7 -

-
-
-
-
-
-
- -
-
-
-
-
- Last updated -
-
-
-
- - 1 minute ago - -
-
-
-
-
-
-
-
-
- -
-
-
-
-
- Created -
-
-
-
- - 1 minute ago - -
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
- - - - Created by - - - -
-
-
-
- -
-
-
- someone -
-
-
-
-
-
-
-
-
- - - - Updated by - - - -
-
-
-
- -
-
-
- someone -
-
-
-
-
-
-
-
-
-
-
- -
-
-
- Applied globally -
-
-
-
-
-
-
- Trusted App 7 -
-
-
-
-
-
- - - - - - OS - - - - - IS - - - - Mac - - - -
-
-
-
-
-
-
-
-
-
-

- trusted app 8 -

-
-
-
-
-
-
- -
-
-
-
-
- Last updated -
-
-
-
- - 1 minute ago - -
-
-
-
-
-
-
-
-
- -
-
-
-
-
- Created -
-
-
-
- - 1 minute ago - -
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
- - - - Created by - - - -
-
-
-
- -
-
-
- someone -
-
-
-
-
-
-
-
-
- - - - Updated by - - - -
-
-
-
- -
-
-
- someone -
-
-
-
-
-
-
-
-
-
-
- -
-
-
- Applied globally -
-
-
-
-
-
-
- Trusted App 8 -
-
-
-
-
-
- - - - - - OS - - - - - IS - - - - Linux - - - -
-
-
-
-
-
-
-
-
-
-

- trusted app 9 -

-
-
-
-
-
-
- -
-
-
-
-
- Last updated -
-
-
-
- - 1 minute ago - -
-
-
-
-
-
-
-
-
- -
-
-
-
-
- Created -
-
-
-
- - 1 minute ago - -
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
- - - - Created by - - - -
-
-
-
- -
-
-
- someone -
-
-
-
-
-
-
-
-
- - - - Updated by - - - -
-
-
-
- -
-
-
- someone -
-
-
-
-
-
-
-
-
-
-
- -
-
-
- Applied globally -
-
-
-
-
-
-
- Trusted App 9 -
-
-
-
-
-
- - - - - - OS - - - - - IS - - - - Windows - - - -
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
- -
-
-
-
-
-
-`; - -exports[`TrustedAppsGrid renders correctly when loading data for the first time 1`] = ` -.c1 { - position: relative; - padding-top: 4px; -} - -.c1 .body { - min-height: 40px; -} - -.c1 .body-content { - position: relative; -} - -.c0 .trusted-app + .trusted-app { - margin-top: 24px; -} - -
-
-
-
-
-
-
-
-
-
-`; - -exports[`TrustedAppsGrid renders correctly when loading data for the second time 1`] = ` -.c1 { - position: relative; - padding-top: 4px; -} - -.c1 .body { - min-height: 40px; -} - -.c1 .body-content { - position: relative; -} - -.c6 { - padding-top: 2px; -} - -.c5 { - margin-bottom: 4px !important; -} - -.c7 { - margin: 6px; -} - -.c3.artifactEntryCard + .c2.artifactEntryCard { - margin-top: 24px; -} - -.c4 { - padding: 32px; -} - -.c4.top-section { - padding-bottom: 24px; -} - -.c4.bottom-section { - padding-top: 24px; -} - -.c4.artifact-entry-collapsible-card { - padding: 24px !important; -} - -.c0 .trusted-app + .trusted-app { - margin-top: 24px; -} - -
-
-
-
-
-
-
-
-
-
-
-
-

- trusted app 0 -

-
-
-
-
-
-
- -
-
-
-
-
- Last updated -
-
-
-
- - 1 minute ago - -
-
-
-
-
-
-
-
-
- -
-
-
-
-
- Created -
-
-
-
- - 1 minute ago - -
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
- - - - Created by - - - -
-
-
-
- -
-
-
- someone -
-
-
-
-
-
-
-
-
- - - - Updated by - - - -
-
-
-
- -
-
-
- someone -
-
-
-
-
-
-
-
-
-
-
- -
-
-
- Applied globally -
-
-
-
-
-
-
- Trusted App 0 -
-
-
-
-
-
- - - - - - OS - - - - - IS - - - - Windows - - - -
-
-
-
-
-
-
-
-
-
-

- trusted app 1 -

-
-
-
-
-
-
- -
-
-
-
-
- Last updated -
-
-
-
- - 1 minute ago - -
-
-
-
-
-
-
-
-
- -
-
-
-
-
- Created -
-
-
-
- - 1 minute ago - -
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
- - - - Created by - - - -
-
-
-
- -
-
-
- someone -
-
-
-
-
-
-
-
-
- - - - Updated by - - - -
-
-
-
- -
-
-
- someone -
-
-
-
-
-
-
-
-
-
-
- -
-
-
- Applied globally -
-
-
-
-
-
-
- Trusted App 1 -
-
-
-
-
-
- - - - - - OS - - - - - IS - - - - Mac - - - -
-
-
-
-
-
-
-
-
-
-

- trusted app 2 -

-
-
-
-
-
-
- -
-
-
-
-
- Last updated -
-
-
-
- - 1 minute ago - -
-
-
-
-
-
-
-
-
- -
-
-
-
-
- Created -
-
-
-
- - 1 minute ago - -
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
- - - - Created by - - - -
-
-
-
- -
-
-
- someone -
-
-
-
-
-
-
-
-
- - - - Updated by - - - -
-
-
-
- -
-
-
- someone -
-
-
-
-
-
-
-
-
-
-
- -
-
-
- Applied globally -
-
-
-
-
-
-
- Trusted App 2 -
-
-
-
-
-
- - - - - - OS - - - - - IS - - - - Linux - - - -
-
-
-
-
-
-
-
-
-
-

- trusted app 3 -

-
-
-
-
-
-
- -
-
-
-
-
- Last updated -
-
-
-
- - 1 minute ago - -
-
-
-
-
-
-
-
-
- -
-
-
-
-
- Created -
-
-
-
- - 1 minute ago - -
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
- - - - Created by - - - -
-
-
-
- -
-
-
- someone -
-
-
-
-
-
-
-
-
- - - - Updated by - - - -
-
-
-
- -
-
-
- someone -
-
-
-
-
-
-
-
-
-
-
- -
-
-
- Applied globally -
-
-
-
-
-
-
- Trusted App 3 -
-
-
-
-
-
- - - - - - OS - - - - - IS - - - - Windows - - - -
-
-
-
-
-
-
-
-
-
-

- trusted app 4 -

-
-
-
-
-
-
- -
-
-
-
-
- Last updated -
-
-
-
- - 1 minute ago - -
-
-
-
-
-
-
-
-
- -
-
-
-
-
- Created -
-
-
-
- - 1 minute ago - -
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
- - - - Created by - - - -
-
-
-
- -
-
-
- someone -
-
-
-
-
-
-
-
-
- - - - Updated by - - - -
-
-
-
- -
-
-
- someone -
-
-
-
-
-
-
-
-
-
-
- -
-
-
- Applied globally -
-
-
-
-
-
-
- Trusted App 4 -
-
-
-
-
-
- - - - - - OS - - - - - IS - - - - Mac - - - -
-
-
-
-
-
-
-
-
-
-

- trusted app 5 -

-
-
-
-
-
-
- -
-
-
-
-
- Last updated -
-
-
-
- - 1 minute ago - -
-
-
-
-
-
-
-
-
- -
-
-
-
-
- Created -
-
-
-
- - 1 minute ago - -
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
- - - - Created by - - - -
-
-
-
- -
-
-
- someone -
-
-
-
-
-
-
-
-
- - - - Updated by - - - -
-
-
-
- -
-
-
- someone -
-
-
-
-
-
-
-
-
-
-
- -
-
-
- Applied globally -
-
-
-
-
-
-
- Trusted App 5 -
-
-
-
-
-
- - - - - - OS - - - - - IS - - - - Linux - - - -
-
-
-
-
-
-
-
-
-
-

- trusted app 6 -

-
-
-
-
-
-
- -
-
-
-
-
- Last updated -
-
-
-
- - 1 minute ago - -
-
-
-
-
-
-
-
-
- -
-
-
-
-
- Created -
-
-
-
- - 1 minute ago - -
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
- - - - Created by - - - -
-
-
-
- -
-
-
- someone -
-
-
-
-
-
-
-
-
- - - - Updated by - - - -
-
-
-
- -
-
-
- someone -
-
-
-
-
-
-
-
-
-
-
- -
-
-
- Applied globally -
-
-
-
-
-
-
- Trusted App 6 -
-
-
-
-
-
- - - - - - OS - - - - - IS - - - - Windows - - - -
-
-
-
-
-
-
-
-
-
-

- trusted app 7 -

-
-
-
-
-
-
- -
-
-
-
-
- Last updated -
-
-
-
- - 1 minute ago - -
-
-
-
-
-
-
-
-
- -
-
-
-
-
- Created -
-
-
-
- - 1 minute ago - -
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
- - - - Created by - - - -
-
-
-
- -
-
-
- someone -
-
-
-
-
-
-
-
-
- - - - Updated by - - - -
-
-
-
- -
-
-
- someone -
-
-
-
-
-
-
-
-
-
-
- -
-
-
- Applied globally -
-
-
-
-
-
-
- Trusted App 7 -
-
-
-
-
-
- - - - - - OS - - - - - IS - - - - Mac - - - -
-
-
-
-
-
-
-
-
-
-

- trusted app 8 -

-
-
-
-
-
-
- -
-
-
-
-
- Last updated -
-
-
-
- - 1 minute ago - -
-
-
-
-
-
-
-
-
- -
-
-
-
-
- Created -
-
-
-
- - 1 minute ago - -
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
- - - - Created by - - - -
-
-
-
- -
-
-
- someone -
-
-
-
-
-
-
-
-
- - - - Updated by - - - -
-
-
-
- -
-
-
- someone -
-
-
-
-
-
-
-
-
-
-
- -
-
-
- Applied globally -
-
-
-
-
-
-
- Trusted App 8 -
-
-
-
-
-
- - - - - - OS - - - - - IS - - - - Linux - - - -
-
-
-
-
-
-
-
-
-
-

- trusted app 9 -

-
-
-
-
-
-
- -
-
-
-
-
- Last updated -
-
-
-
- - 1 minute ago - -
-
-
-
-
-
-
-
-
- -
-
-
-
-
- Created -
-
-
-
- - 1 minute ago - -
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
- - - - Created by - - - -
-
-
-
- -
-
-
- someone -
-
-
-
-
-
-
-
-
- - - - Updated by - - - -
-
-
-
- -
-
-
- someone -
-
-
-
-
-
-
-
-
-
-
- -
-
-
- Applied globally -
-
-
-
-
-
-
- Trusted App 9 -
-
-
-
-
-
- - - - - - OS - - - - - IS - - - - Windows - - - -
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
- -
-
-
-
-
-
-`; - -exports[`TrustedAppsGrid renders correctly when new page and page size set (not loading yet) 1`] = ` -.c1 { - position: relative; - padding-top: 4px; -} - -.c1 .body { - min-height: 40px; -} - -.c1 .body-content { - position: relative; -} - -.c6 { - padding-top: 2px; -} - -.c5 { - margin-bottom: 4px !important; -} - -.c7 { - margin: 6px; -} - -.c3.artifactEntryCard + .c2.artifactEntryCard { - margin-top: 24px; -} - -.c4 { - padding: 32px; -} - -.c4.top-section { - padding-bottom: 24px; -} - -.c4.bottom-section { - padding-top: 24px; -} - -.c4.artifact-entry-collapsible-card { - padding: 24px !important; -} - -.c0 .trusted-app + .trusted-app { - margin-top: 24px; -} - -
-
-
-
-
-
-
-
-
-
-
-

- trusted app 0 -

-
-
-
-
-
-
- -
-
-
-
-
- Last updated -
-
-
-
- - 1 minute ago - -
-
-
-
-
-
-
-
-
- -
-
-
-
-
- Created -
-
-
-
- - 1 minute ago - -
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
- - - - Created by - - - -
-
-
-
- -
-
-
- someone -
-
-
-
-
-
-
-
-
- - - - Updated by - - - -
-
-
-
- -
-
-
- someone -
-
-
-
-
-
-
-
-
-
-
- -
-
-
- Applied globally -
-
-
-
-
-
-
- Trusted App 0 -
-
-
-
-
-
- - - - - - OS - - - - - IS - - - - Windows - - - -
-
-
-
-
-
-
-
-
-
-

- trusted app 1 -

-
-
-
-
-
-
- -
-
-
-
-
- Last updated -
-
-
-
- - 1 minute ago - -
-
-
-
-
-
-
-
-
- -
-
-
-
-
- Created -
-
-
-
- - 1 minute ago - -
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
- - - - Created by - - - -
-
-
-
- -
-
-
- someone -
-
-
-
-
-
-
-
-
- - - - Updated by - - - -
-
-
-
- -
-
-
- someone -
-
-
-
-
-
-
-
-
-
-
- -
-
-
- Applied globally -
-
-
-
-
-
-
- Trusted App 1 -
-
-
-
-
-
- - - - - - OS - - - - - IS - - - - Mac - - - -
-
-
-
-
-
-
-
-
-
-

- trusted app 2 -

-
-
-
-
-
-
- -
-
-
-
-
- Last updated -
-
-
-
- - 1 minute ago - -
-
-
-
-
-
-
-
-
- -
-
-
-
-
- Created -
-
-
-
- - 1 minute ago - -
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
- - - - Created by - - - -
-
-
-
- -
-
-
- someone -
-
-
-
-
-
-
-
-
- - - - Updated by - - - -
-
-
-
- -
-
-
- someone -
-
-
-
-
-
-
-
-
-
-
- -
-
-
- Applied globally -
-
-
-
-
-
-
- Trusted App 2 -
-
-
-
-
-
- - - - - - OS - - - - - IS - - - - Linux - - - -
-
-
-
-
-
-
-
-
-
-

- trusted app 3 -

-
-
-
-
-
-
- -
-
-
-
-
- Last updated -
-
-
-
- - 1 minute ago - -
-
-
-
-
-
-
-
-
- -
-
-
-
-
- Created -
-
-
-
- - 1 minute ago - -
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
- - - - Created by - - - -
-
-
-
- -
-
-
- someone -
-
-
-
-
-
-
-
-
- - - - Updated by - - - -
-
-
-
- -
-
-
- someone -
-
-
-
-
-
-
-
-
-
-
- -
-
-
- Applied globally -
-
-
-
-
-
-
- Trusted App 3 -
-
-
-
-
-
- - - - - - OS - - - - - IS - - - - Windows - - - -
-
-
-
-
-
-
-
-
-
-

- trusted app 4 -

-
-
-
-
-
-
- -
-
-
-
-
- Last updated -
-
-
-
- - 1 minute ago - -
-
-
-
-
-
-
-
-
- -
-
-
-
-
- Created -
-
-
-
- - 1 minute ago - -
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
- - - - Created by - - - -
-
-
-
- -
-
-
- someone -
-
-
-
-
-
-
-
-
- - - - Updated by - - - -
-
-
-
- -
-
-
- someone -
-
-
-
-
-
-
-
-
-
-
- -
-
-
- Applied globally -
-
-
-
-
-
-
- Trusted App 4 -
-
-
-
-
-
- - - - - - OS - - - - - IS - - - - Mac - - - -
-
-
-
-
-
-
-
-
-
-

- trusted app 5 -

-
-
-
-
-
-
- -
-
-
-
-
- Last updated -
-
-
-
- - 1 minute ago - -
-
-
-
-
-
-
-
-
- -
-
-
-
-
- Created -
-
-
-
- - 1 minute ago - -
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
- - - - Created by - - - -
-
-
-
- -
-
-
- someone -
-
-
-
-
-
-
-
-
- - - - Updated by - - - -
-
-
-
- -
-
-
- someone -
-
-
-
-
-
-
-
-
-
-
- -
-
-
- Applied globally -
-
-
-
-
-
-
- Trusted App 5 -
-
-
-
-
-
- - - - - - OS - - - - - IS - - - - Linux - - - -
-
-
-
-
-
-
-
-
-
-

- trusted app 6 -

-
-
-
-
-
-
- -
-
-
-
-
- Last updated -
-
-
-
- - 1 minute ago - -
-
-
-
-
-
-
-
-
- -
-
-
-
-
- Created -
-
-
-
- - 1 minute ago - -
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
- - - - Created by - - - -
-
-
-
- -
-
-
- someone -
-
-
-
-
-
-
-
-
- - - - Updated by - - - -
-
-
-
- -
-
-
- someone -
-
-
-
-
-
-
-
-
-
-
- -
-
-
- Applied globally -
-
-
-
-
-
-
- Trusted App 6 -
-
-
-
-
-
- - - - - - OS - - - - - IS - - - - Windows - - - -
-
-
-
-
-
-
-
-
-
-

- trusted app 7 -

-
-
-
-
-
-
- -
-
-
-
-
- Last updated -
-
-
-
- - 1 minute ago - -
-
-
-
-
-
-
-
-
- -
-
-
-
-
- Created -
-
-
-
- - 1 minute ago - -
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
- - - - Created by - - - -
-
-
-
- -
-
-
- someone -
-
-
-
-
-
-
-
-
- - - - Updated by - - - -
-
-
-
- -
-
-
- someone -
-
-
-
-
-
-
-
-
-
-
- -
-
-
- Applied globally -
-
-
-
-
-
-
- Trusted App 7 -
-
-
-
-
-
- - - - - - OS - - - - - IS - - - - Mac - - - -
-
-
-
-
-
-
-
-
-
-

- trusted app 8 -

-
-
-
-
-
-
- -
-
-
-
-
- Last updated -
-
-
-
- - 1 minute ago - -
-
-
-
-
-
-
-
-
- -
-
-
-
-
- Created -
-
-
-
- - 1 minute ago - -
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
- - - - Created by - - - -
-
-
-
- -
-
-
- someone -
-
-
-
-
-
-
-
-
- - - - Updated by - - - -
-
-
-
- -
-
-
- someone -
-
-
-
-
-
-
-
-
-
-
- -
-
-
- Applied globally -
-
-
-
-
-
-
- Trusted App 8 -
-
-
-
-
-
- - - - - - OS - - - - - IS - - - - Linux - - - -
-
-
-
-
-
-
-
-
-
-

- trusted app 9 -

-
-
-
-
-
-
- -
-
-
-
-
- Last updated -
-
-
-
- - 1 minute ago - -
-
-
-
-
-
-
-
-
- -
-
-
-
-
- Created -
-
-
-
- - 1 minute ago - -
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
- - - - Created by - - - -
-
-
-
- -
-
-
- someone -
-
-
-
-
-
-
-
-
- - - - Updated by - - - -
-
-
-
- -
-
-
- someone -
-
-
-
-
-
-
-
-
-
-
- -
-
-
- Applied globally -
-
-
-
-
-
-
- Trusted App 9 -
-
-
-
-
-
- - - - - - OS - - - - - IS - - - - Windows - - - -
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
- -
-
-
-
-
-
-`; diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_grid/index.stories.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_grid/index.stories.tsx deleted file mode 100644 index 682d689c7e82..000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_grid/index.stories.tsx +++ /dev/null @@ -1,99 +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 React from 'react'; -import { Provider } from 'react-redux'; -import { ThemeProvider } from 'styled-components'; -import { storiesOf } from '@storybook/react'; -import { euiLightVars } from '@kbn/ui-theme'; -import { EuiHorizontalRule } from '@elastic/eui'; - -import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; - -import { - createGlobalNoMiddlewareStore, - createListFailedResourceState, - createListLoadedResourceState, - createListLoadingResourceState, - createTrustedAppsListResourceStateChangedAction, -} from '../../../test_utils'; - -import { TrustedAppsGrid } from '.'; - -const now = 111111; - -const renderGrid = (store: ReturnType) => ( - - 'MMM D, YYYY @ HH:mm:ss.SSS' } }}> - ({ eui: euiLightVars, darkMode: false })}> - - - - - - - - -); - -storiesOf('TrustedApps/TrustedAppsGrid', module) - .add('default', () => { - return renderGrid(createGlobalNoMiddlewareStore()); - }) - .add('loading', () => { - const store = createGlobalNoMiddlewareStore(); - - store.dispatch( - createTrustedAppsListResourceStateChangedAction(createListLoadingResourceState()) - ); - - return renderGrid(store); - }) - .add('error', () => { - const store = createGlobalNoMiddlewareStore(); - - store.dispatch( - createTrustedAppsListResourceStateChangedAction( - createListFailedResourceState('Intenal Server Error') - ) - ); - - return renderGrid(store); - }) - .add('loaded', () => { - const store = createGlobalNoMiddlewareStore(); - - store.dispatch( - createTrustedAppsListResourceStateChangedAction( - createListLoadedResourceState({ pageSize: 10 }, now) - ) - ); - - return renderGrid(store); - }) - .add('loading second time', () => { - const store = createGlobalNoMiddlewareStore(); - - store.dispatch( - createTrustedAppsListResourceStateChangedAction( - createListLoadingResourceState(createListLoadedResourceState({ pageSize: 10 }, now)) - ) - ); - - return renderGrid(store); - }) - .add('long texts', () => { - const store = createGlobalNoMiddlewareStore(); - - store.dispatch( - createTrustedAppsListResourceStateChangedAction( - createListLoadedResourceState({ pageSize: 10 }, now, true) - ) - ); - - return renderGrid(store); - }); diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_grid/index.test.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_grid/index.test.tsx deleted file mode 100644 index 6cc292b5a6d3..000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_grid/index.test.tsx +++ /dev/null @@ -1,150 +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 { render, act } from '@testing-library/react'; -import React from 'react'; -import { Provider } from 'react-redux'; - -import { - createSampleTrustedApp, - createListFailedResourceState, - createListLoadedResourceState, - createListLoadingResourceState, - createTrustedAppsListResourceStateChangedAction, - createUserChangedUrlAction, - createGlobalNoMiddlewareStore, -} from '../../../test_utils'; -import { TrustedAppsGrid } from '.'; -import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common'; - -jest.mock('@elastic/eui/lib/services/accessibility/html_id_generator', () => ({ - htmlIdGenerator: () => () => 'mockId', -})); - -jest.mock('../../../../../../common/lib/kibana'); - -const now = 111111; - -const renderList = (store: ReturnType) => { - const Wrapper: React.FC = ({ children }) => ( - - {children} - - ); - - return render(, { wrapper: Wrapper }); -}; - -describe('TrustedAppsGrid', () => { - it('renders correctly initially', () => { - expect(renderList(createGlobalNoMiddlewareStore()).container).toMatchSnapshot(); - }); - - it('renders correctly when loading data for the first time', () => { - const store = createGlobalNoMiddlewareStore(); - - store.dispatch( - createTrustedAppsListResourceStateChangedAction(createListLoadingResourceState()) - ); - - expect(renderList(store).container).toMatchSnapshot(); - }); - - it('renders correctly when failed loading data for the first time', () => { - const store = createGlobalNoMiddlewareStore(); - - store.dispatch( - createTrustedAppsListResourceStateChangedAction( - createListFailedResourceState('Intenal Server Error') - ) - ); - - expect(renderList(store).container).toMatchSnapshot(); - }); - - it('renders correctly when loaded data', () => { - const store = createGlobalNoMiddlewareStore(); - - store.dispatch( - createTrustedAppsListResourceStateChangedAction( - createListLoadedResourceState({ pageSize: 10 }, now) - ) - ); - - expect(renderList(store).container).toMatchSnapshot(); - }); - - it('renders correctly when new page and page size set (not loading yet)', () => { - const store = createGlobalNoMiddlewareStore(); - - store.dispatch( - createTrustedAppsListResourceStateChangedAction( - createListLoadedResourceState({ pageSize: 10 }, now) - ) - ); - store.dispatch( - createUserChangedUrlAction('/administration/trusted_apps', '?page_index=2&page_size=50') - ); - - expect(renderList(store).container).toMatchSnapshot(); - }); - - it('renders correctly when loading data for the second time', () => { - const store = createGlobalNoMiddlewareStore(); - - store.dispatch( - createTrustedAppsListResourceStateChangedAction( - createListLoadingResourceState(createListLoadedResourceState({ pageSize: 10 }, now)) - ) - ); - - expect(renderList(store).container).toMatchSnapshot(); - }); - - it('renders correctly when failed loading data for the second time', () => { - const store = createGlobalNoMiddlewareStore(); - - store.dispatch( - createTrustedAppsListResourceStateChangedAction( - createListFailedResourceState( - 'Intenal Server Error', - createListLoadedResourceState({ pageSize: 10 }, now) - ) - ) - ); - - expect(renderList(store).container).toMatchSnapshot(); - }); - - it('triggers deletion dialog when delete action clicked', async () => { - const store = createGlobalNoMiddlewareStore(); - - store.dispatch( - createTrustedAppsListResourceStateChangedAction( - createListLoadedResourceState({ pageSize: 10 }, now) - ) - ); - store.dispatch = jest.fn(); - - const renderResult = renderList(store); - - await act(async () => { - (await renderResult.findAllByTestId('trustedAppCard-header-actions-button'))[0].click(); - }); - - await act(async () => { - (await renderResult.findByTestId('deleteTrustedAppAction')).click(); - }); - - expect(store.dispatch).toBeCalledWith({ - type: 'trustedAppDeletionDialogStarted', - payload: { - entry: createSampleTrustedApp(0), - }, - }); - }); -}); diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_grid/index.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_grid/index.tsx deleted file mode 100644 index a8e218708352..000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_grid/index.tsx +++ /dev/null @@ -1,210 +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 React, { memo, useCallback, useMemo } from 'react'; - -import { useHistory } from 'react-router-dom'; -import styled from 'styled-components'; -import { i18n } from '@kbn/i18n'; -import { useDispatch } from 'react-redux'; -import { Dispatch } from 'redux'; -import { Pagination } from '../../../state'; - -import { - getCurrentLocation, - getListErrorMessage, - getListItems, - getListPagination, - isListLoading, - getMapOfPoliciesById, - isLoadingListOfPolicies, -} from '../../../store/selectors'; - -import { useTrustedAppsNavigateCallback, useTrustedAppsSelector } from '../../hooks'; - -import { getPolicyDetailPath, getTrustedAppsListPath } from '../../../../../common/routing'; -import { - PaginatedContent, - PaginatedContentProps, -} from '../../../../../components/paginated_content'; -import { PolicyDetailsRouteState, TrustedApp } from '../../../../../../../common/endpoint/types'; -import { - ArtifactEntryCard, - ArtifactEntryCardProps, -} from '../../../../../components/artifact_entry_card'; -import { AppAction } from '../../../../../../common/store/actions'; -import { APP_UI_ID } from '../../../../../../../common/constants'; -import { useAppUrl } from '../../../../../../common/lib/kibana'; - -export interface PaginationBarProps { - pagination: Pagination; - onChange: (pagination: { size: number; index: number }) => void; -} - -type ArtifactEntryCardType = typeof ArtifactEntryCard; - -const RootWrapper = styled.div` - .trusted-app + .trusted-app { - margin-top: ${({ theme }) => theme.eui.spacerSizes.l}; - } -`; - -const BACK_TO_TRUSTED_APPS_LABEL = i18n.translate( - 'xpack.securitySolution.trustedapps.grid.policyDetailsLinkBackLabel', - { defaultMessage: 'Back to trusted applications' } -); - -const EDIT_TRUSTED_APP_ACTION_LABEL = i18n.translate( - 'xpack.securitySolution.trustedapps.grid.cardAction.edit', - { - defaultMessage: 'Edit trusted application', - } -); - -const DELETE_TRUSTED_APP_ACTION_LABEL = i18n.translate( - 'xpack.securitySolution.trustedapps.grid.cardAction.delete', - { - defaultMessage: 'Delete trusted application', - } -); - -export const TrustedAppsGrid = memo(() => { - const history = useHistory(); - const dispatch = useDispatch>(); - const { getAppUrl } = useAppUrl(); - - const pagination = useTrustedAppsSelector(getListPagination); - const listItems = useTrustedAppsSelector(getListItems); - const isLoading = useTrustedAppsSelector(isListLoading); - const error = useTrustedAppsSelector(getListErrorMessage); - const location = useTrustedAppsSelector(getCurrentLocation); - const policyListById = useTrustedAppsSelector(getMapOfPoliciesById); - const loadingPoliciesList = useTrustedAppsSelector(isLoadingListOfPolicies); - - const handlePaginationChange: PaginatedContentProps< - TrustedApp, - ArtifactEntryCardType - >['onChange'] = useTrustedAppsNavigateCallback(({ pageIndex, pageSize }) => ({ - page_index: pageIndex, - page_size: pageSize, - })); - - const artifactCardPropsPerItem = useMemo(() => { - const cachedCardProps: Record = {}; - - // Casting `listItems` below to remove the `Immutable<>` from it in order to prevent errors - // with common component's props - for (const trustedApp of listItems as TrustedApp[]) { - let policies: ArtifactEntryCardProps['policies']; - - if (trustedApp.effectScope.type === 'policy' && trustedApp.effectScope.policies.length) { - policies = trustedApp.effectScope.policies.reduce< - Required['policies'] - >((policyToNavOptionsMap, policyId) => { - const currentPagePath = getTrustedAppsListPath({ - ...location, - }); - - const policyDetailsPath = getPolicyDetailPath(policyId); - - const routeState: PolicyDetailsRouteState = { - backLink: { - label: BACK_TO_TRUSTED_APPS_LABEL, - navigateTo: [ - APP_UI_ID, - { - path: currentPagePath, - }, - ], - href: getAppUrl({ path: currentPagePath }), - }, - onCancelNavigateTo: [ - APP_UI_ID, - { - path: currentPagePath, - }, - ], - }; - - policyToNavOptionsMap[policyId] = { - navigateAppId: APP_UI_ID, - navigateOptions: { - path: policyDetailsPath, - state: routeState, - }, - href: getAppUrl({ path: policyDetailsPath }), - children: policyListById[policyId]?.name ?? policyId, - target: '_blank', - }; - return policyToNavOptionsMap; - }, {}); - } - - cachedCardProps[trustedApp.id] = { - item: trustedApp, - policies, - loadingPoliciesList, - hideComments: true, - 'data-test-subj': 'trustedAppCard', - actions: [ - { - icon: 'controlsHorizontal', - onClick: () => { - history.push( - getTrustedAppsListPath({ - ...location, - show: 'edit', - id: trustedApp.id, - }) - ); - }, - 'data-test-subj': 'editTrustedAppAction', - children: EDIT_TRUSTED_APP_ACTION_LABEL, - }, - { - icon: 'trash', - onClick: () => { - dispatch({ - type: 'trustedAppDeletionDialogStarted', - payload: { entry: trustedApp }, - }); - }, - 'data-test-subj': 'deleteTrustedAppAction', - children: DELETE_TRUSTED_APP_ACTION_LABEL, - }, - ], - hideDescription: !trustedApp.description, - }; - } - - return cachedCardProps; - }, [dispatch, getAppUrl, history, listItems, location, policyListById, loadingPoliciesList]); - - const handleArtifactCardProps = useCallback( - (trustedApp: TrustedApp) => { - return artifactCardPropsPerItem[trustedApp.id]; - }, - [artifactCardPropsPerItem] - ); - - return ( - - - items={listItems as TrustedApp[]} - onChange={handlePaginationChange} - ItemComponent={ArtifactEntryCard} - itemComponentProps={handleArtifactCardProps} - loading={isLoading} - itemId="id" - error={error} - pagination={pagination} - /> - - ); -}); - -TrustedAppsGrid.displayName = 'TrustedAppsGrid'; diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/view_type_toggle/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/view_type_toggle/__snapshots__/index.test.tsx.snap deleted file mode 100644 index 09a13f11d2ad..000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/view_type_toggle/__snapshots__/index.test.tsx.snap +++ /dev/null @@ -1,53 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`view_type_toggle ViewTypeToggle should render grid selection correctly 1`] = ` - -`; - -exports[`view_type_toggle ViewTypeToggle should render list selection correctly 1`] = ` - -`; diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/view_type_toggle/index.stories.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/view_type_toggle/index.stories.tsx deleted file mode 100644 index 8ba70769838a..000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/view_type_toggle/index.stories.tsx +++ /dev/null @@ -1,28 +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 React, { useState } from 'react'; -import { ThemeProvider } from 'styled-components'; -import { storiesOf, addDecorator } from '@storybook/react'; -import { euiLightVars } from '@kbn/ui-theme'; - -import { ViewType } from '../../../state'; -import { ViewTypeToggle } from '.'; - -addDecorator((storyFn) => ( - ({ eui: euiLightVars, darkMode: false })}>{storyFn()} -)); - -const useRenderStory = (viewType: ViewType) => { - const [selectedOption, setSelectedOption] = useState(viewType); - - return ; -}; - -storiesOf('TrustedApps/ViewTypeToggle', module) - .add('grid selected', () => useRenderStory('grid')) - .add('list selected', () => useRenderStory('list')); diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/view_type_toggle/index.test.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/view_type_toggle/index.test.tsx deleted file mode 100644 index d6b2bb5a2e7e..000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/view_type_toggle/index.test.tsx +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { render } from '@testing-library/react'; -import { shallow } from 'enzyme'; -import React from 'react'; - -import { ViewTypeToggle } from '.'; - -describe('view_type_toggle', () => { - describe('ViewTypeToggle', () => { - it('should render grid selection correctly', () => { - const element = shallow( {}} />); - - expect(element).toMatchSnapshot(); - }); - - it('should render list selection correctly', () => { - const element = shallow( {}} />); - - expect(element).toMatchSnapshot(); - }); - - it('should trigger onToggle', async () => { - const onToggle = jest.fn(); - const element = render(); - - (await element.findAllByTestId('viewTypeToggleButton'))[0].click(); - - expect(onToggle).toBeCalledWith('grid'); - }); - }); -}); diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/view_type_toggle/index.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/view_type_toggle/index.tsx deleted file mode 100644 index 700358d6beef..000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/view_type_toggle/index.tsx +++ /dev/null @@ -1,57 +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 React, { memo, useCallback } from 'react'; -import { EuiButtonGroup } from '@elastic/eui'; - -import { i18n } from '@kbn/i18n'; -import { ViewType } from '../../../state'; -import { GRID_VIEW_TOGGLE_LABEL, LIST_VIEW_TOGGLE_LABEL } from '../../translations'; - -export interface ViewTypeToggleProps { - selectedOption: ViewType; - onToggle: (type: ViewType) => void; -} - -export const ViewTypeToggle = memo(({ selectedOption, onToggle }: ViewTypeToggleProps) => { - const handleChange = useCallback( - (id) => { - if (id === 'list' || id === 'grid') { - onToggle(id); - } - }, - [onToggle] - ); - - return ( - - ); -}); - -ViewTypeToggle.displayName = 'ViewTypeToggle'; diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/hooks.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/hooks.ts deleted file mode 100644 index 4c2747d74d31..000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/hooks.ts +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { useDispatch, useSelector } from 'react-redux'; -import { useHistory } from 'react-router-dom'; -import { useCallback } from 'react'; - -import { State } from '../../../../common/store'; - -import { - MANAGEMENT_STORE_TRUSTED_APPS_NAMESPACE as TRUSTED_APPS_NS, - MANAGEMENT_STORE_GLOBAL_NAMESPACE as GLOBAL_NS, -} from '../../../common/constants'; - -import { AppAction } from '../../../../common/store/actions'; -import { getTrustedAppsListPath } from '../../../common/routing'; -import { TrustedAppsListPageLocation, TrustedAppsListPageState } from '../state'; -import { getCurrentLocation } from '../store/selectors'; - -export function useTrustedAppsSelector(selector: (state: TrustedAppsListPageState) => R): R { - return useSelector((state: State) => - selector(state[GLOBAL_NS][TRUSTED_APPS_NS] as TrustedAppsListPageState) - ); -} - -export type NavigationCallback = ( - ...args: Parameters[0]> -) => Partial; - -export function useTrustedAppsNavigateCallback(callback: NavigationCallback) { - const location = useTrustedAppsSelector(getCurrentLocation); - const history = useHistory(); - - return useCallback( - (...args) => history.push(getTrustedAppsListPath({ ...location, ...callback(...args) })), - // TODO: needs more investigation, but if callback is in dependencies list memoization will never happen - // eslint-disable-next-line react-hooks/exhaustive-deps - [history, location] - ); -} - -export function useTrustedAppsStoreActionCallback( - callback: (...args: Parameters[0]>) => AppAction -) { - const dispatch = useDispatch(); - - // eslint-disable-next-line react-hooks/exhaustive-deps - return useCallback((...args) => dispatch(callback(...args)), [dispatch]); -} diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/translations.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/translations.ts index 3d8a56ad7431..b5b92f3a686e 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/translations.ts +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/translations.ts @@ -13,14 +13,54 @@ import { OperatorFieldIds, } from '../../../../../common/endpoint/types'; -export { OS_TITLES } from '../../../common/translations'; - export const ABOUT_TRUSTED_APPS = i18n.translate('xpack.securitySolution.trustedapps.aboutInfo', { defaultMessage: 'Add a trusted application to improve performance or alleviate conflicts with other applications running on ' + 'your hosts.', }); +export const NAME_LABEL = i18n.translate('xpack.securitySolution.trustedApps.name.label', { + defaultMessage: 'Name', +}); + +export const DETAILS_HEADER = i18n.translate('xpack.securitySolution.trustedApps.details.header', { + defaultMessage: 'Details', +}); + +export const DETAILS_HEADER_DESCRIPTION = i18n.translate( + 'xpack.securitySolution.trustedApps.details.header.description', + { + defaultMessage: + 'Trusted applications improve performance or alleviate conflicts with other applications running on your hosts.', + } +); + +export const DESCRIPTION_LABEL = i18n.translate( + 'xpack.securitySolution.trustedapps.create.description', + { + defaultMessage: 'Description', + } +); + +export const CONDITIONS_HEADER = i18n.translate( + 'xpack.securitySolution.trustedApps.conditions.header', + { + defaultMessage: 'Conditions', + } +); + +export const CONDITIONS_HEADER_DESCRIPTION = i18n.translate( + 'xpack.securitySolution.trustedApps.conditions.header.description', + { + defaultMessage: + 'Select an operating system and add conditions. Availability of conditions may depend on your chosen OS.', + } +); + +export const SELECT_OS_LABEL = i18n.translate('xpack.securitySolution.trustedApps.os.label', { + defaultMessage: 'Select operating system', +}); + export const CONDITION_FIELD_TITLE: { [K in ConditionEntryField]: string } = { [ConditionEntryField.HASH]: i18n.translate( 'xpack.securitySolution.trustedapps.logicalConditionBuilder.entry.field.hash', @@ -74,23 +114,50 @@ export const ENTRY_PROPERTY_TITLES: Readonly<{ }), }; -export const GRID_VIEW_TOGGLE_LABEL = i18n.translate( - 'xpack.securitySolution.trustedapps.view.toggle.grid', +export const POLICY_SELECT_DESCRIPTION = i18n.translate( + 'xpack.securitySolution.trustedApps.assignmentSectionDescription', { - defaultMessage: 'Grid view', + defaultMessage: + 'Assign this trusted application globally across all policies, or assign it to specific policies.', } ); -export const LIST_VIEW_TOGGLE_LABEL = i18n.translate( - 'xpack.securitySolution.trustedapps.view.toggle.list', - { - defaultMessage: 'List view', - } -); - -export const SEARCH_TRUSTED_APP_PLACEHOLDER = i18n.translate( - 'xpack.securitySolution.trustedapps.list.search.placeholder', - { - defaultMessage: 'Search on the fields below: name, description, value', - } -); +export const INPUT_ERRORS = { + name: i18n.translate('xpack.securitySolution.trustedapps.create.nameRequiredMsg', { + defaultMessage: 'Name is required', + }), + os: i18n.translate('xpack.securitySolution.trustedapps.create.osRequiredMsg', { + defaultMessage: 'Operating System is required', + }), + field: i18n.translate('xpack.securitySolution.trustedapps.create.conditionRequiredMsg', { + defaultMessage: 'At least one Field definition is required', + }), + noDuplicateField: (field: ConditionEntryField) => + i18n.translate('xpack.securitySolution.trustedapps.create.conditionFieldDuplicatedMsg', { + defaultMessage: '{field} cannot be added more than once', + values: { field: CONDITION_FIELD_TITLE[field] }, + }), + mustHaveValue: (index: number) => + i18n.translate('xpack.securitySolution.trustedapps.create.conditionFieldValueRequiredMsg', { + defaultMessage: '[{row}] Field entry must have a value', + values: { row: index + 1 }, + }), + invalidHash: (index: number) => + i18n.translate('xpack.securitySolution.trustedapps.create.conditionFieldInvalidHashMsg', { + defaultMessage: '[{row}] Invalid hash value', + values: { row: index + 1 }, + }), + pathWarning: (index: number) => + i18n.translate('xpack.securitySolution.trustedapps.create.conditionFieldInvalidPathMsg', { + defaultMessage: '[{row}] Path may be formed incorrectly; verify value', + values: { row: index + 1 }, + }), + wildcardPathWarning: (index: number) => + i18n.translate( + 'xpack.securitySolution.trustedapps.create.conditionFieldDegradedPerformanceMsg', + { + defaultMessage: `[{row}] A wildcard in the filename will affect the endpoint's performance`, + values: { row: index + 1 }, + } + ), +}; diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_app_deletion_dialog.test.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_app_deletion_dialog.test.tsx deleted file mode 100644 index 6abce21d7ccf..000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_app_deletion_dialog.test.tsx +++ /dev/null @@ -1,127 +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 React from 'react'; -import { Provider } from 'react-redux'; -import { render } from '@testing-library/react'; -import { I18nProvider } from '@kbn/i18n-react'; - -import { - createGlobalNoMiddlewareStore, - createSampleTrustedApp, - createServerApiError, -} from '../test_utils'; - -import { - TrustedAppDeletionDialogStarted, - TrustedAppDeletionSubmissionResourceStateChanged, -} from '../store/action'; - -import { TrustedAppDeletionDialog } from './trusted_app_deletion_dialog'; - -const renderDeletionDialog = (store: ReturnType) => { - const Wrapper: React.FC = ({ children }) => ( - - {children} - - ); - - return render(, { wrapper: Wrapper }); -}; - -const createDialogStartAction = (): TrustedAppDeletionDialogStarted => ({ - type: 'trustedAppDeletionDialogStarted', - payload: { entry: createSampleTrustedApp(3) }, -}); - -const createDialogLoadingAction = (): TrustedAppDeletionSubmissionResourceStateChanged => ({ - type: 'trustedAppDeletionSubmissionResourceStateChanged', - payload: { - newState: { - type: 'LoadingResourceState', - previousState: { type: 'UninitialisedResourceState' }, - }, - }, -}); - -const createDialogFailedAction = (): TrustedAppDeletionSubmissionResourceStateChanged => ({ - type: 'trustedAppDeletionSubmissionResourceStateChanged', - payload: { - newState: { type: 'FailedResourceState', error: createServerApiError('Not Found') }, - }, -}); - -describe('TrustedAppDeletionDialog', () => { - it('renders correctly initially', () => { - expect(renderDeletionDialog(createGlobalNoMiddlewareStore()).baseElement).toMatchSnapshot(); - }); - - it('renders correctly when dialog started', () => { - const store = createGlobalNoMiddlewareStore(); - - store.dispatch(createDialogStartAction()); - - expect(renderDeletionDialog(store).baseElement).toMatchSnapshot(); - }); - - it('renders correctly when deletion is in progress', () => { - const store = createGlobalNoMiddlewareStore(); - - store.dispatch(createDialogStartAction()); - store.dispatch(createDialogLoadingAction()); - - expect(renderDeletionDialog(store).baseElement).toMatchSnapshot(); - }); - - it('renders correctly when deletion failed', () => { - const store = createGlobalNoMiddlewareStore(); - - store.dispatch(createDialogStartAction()); - store.dispatch(createDialogFailedAction()); - - expect(renderDeletionDialog(store).baseElement).toMatchSnapshot(); - }); - - it('triggers confirmation action when confirm button clicked', async () => { - const store = createGlobalNoMiddlewareStore(); - - store.dispatch(createDialogStartAction()); - store.dispatch = jest.fn(); - - (await renderDeletionDialog(store).findByTestId('trustedAppDeletionConfirm')).click(); - - expect(store.dispatch).toBeCalledWith({ - type: 'trustedAppDeletionDialogConfirmed', - }); - }); - - it('triggers closing action when cancel button clicked', async () => { - const store = createGlobalNoMiddlewareStore(); - - store.dispatch(createDialogStartAction()); - store.dispatch = jest.fn(); - - (await renderDeletionDialog(store).findByTestId('trustedAppDeletionCancel')).click(); - - expect(store.dispatch).toBeCalledWith({ - type: 'trustedAppDeletionDialogClosed', - }); - }); - - it('does not trigger closing action when deletion in progress and cancel button clicked', async () => { - const store = createGlobalNoMiddlewareStore(); - - store.dispatch(createDialogStartAction()); - store.dispatch(createDialogLoadingAction()); - - store.dispatch = jest.fn(); - - (await renderDeletionDialog(store).findByTestId('trustedAppDeletionCancel')).click(); - - expect(store.dispatch).not.toBeCalled(); - }); -}); diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_app_deletion_dialog.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_app_deletion_dialog.tsx deleted file mode 100644 index 458a83d37322..000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_app_deletion_dialog.tsx +++ /dev/null @@ -1,136 +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 { - EuiButtonEmpty, - EuiCallOut, - EuiModal, - EuiModalBody, - EuiModalFooter, - EuiModalHeader, - EuiModalHeaderTitle, - EuiSpacer, - EuiText, -} from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n-react'; -import React, { memo, useCallback, useMemo } from 'react'; -import { useDispatch } from 'react-redux'; -import { Dispatch } from 'redux'; -import { AutoFocusButton } from '../../../../common/components/autofocus_button/autofocus_button'; -import { Immutable, TrustedApp } from '../../../../../common/endpoint/types'; -import { AppAction } from '../../../../common/store/actions'; -import { isPolicyEffectScope } from '../state/type_guards'; -import { - getDeletionDialogEntry, - isDeletionDialogOpen, - isDeletionInProgress, -} from '../store/selectors'; -import { useTrustedAppsSelector } from './hooks'; - -const CANCEL_SUBJ = 'trustedAppDeletionCancel'; -const CONFIRM_SUBJ = 'trustedAppDeletionConfirm'; - -const getTranslations = (entry: Immutable | undefined) => ({ - title: ( - {entry?.name} }} - /> - ), - calloutTitle: ( - - ), - calloutMessage: ( - - ), - subMessage: ( - - ), - cancelButton: ( - - ), - confirmButton: ( - - ), -}); - -export const TrustedAppDeletionDialog = memo(() => { - const dispatch = useDispatch>(); - const isBusy = useTrustedAppsSelector(isDeletionInProgress); - const entry = useTrustedAppsSelector(getDeletionDialogEntry); - const translations = useMemo(() => getTranslations(entry), [entry]); - const onConfirm = useCallback(() => { - dispatch({ type: 'trustedAppDeletionDialogConfirmed' }); - }, [dispatch]); - const onCancel = useCallback(() => { - if (!isBusy) { - dispatch({ type: 'trustedAppDeletionDialogClosed' }); - } - }, [dispatch, isBusy]); - - if (useTrustedAppsSelector(isDeletionDialogOpen)) { - return ( - - - {translations.title} - - - - -

{translations.calloutMessage}

-
- - -

{translations.subMessage}

-
-
- - - - {translations.cancelButton} - - - - {translations.confirmButton} - - -
- ); - } else { - return <>; - } -}); - -TrustedAppDeletionDialog.displayName = 'TrustedAppDeletionDialog'; diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_list.test.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_list.test.tsx new file mode 100644 index 000000000000..f778e6f471e1 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_list.test.tsx @@ -0,0 +1,62 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { act, waitFor } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import React from 'react'; +import { TRUSTED_APPS_PATH } from '../../../../../common/constants'; +import { AppContextTestRender, createAppRootMockRenderer } from '../../../../common/mock/endpoint'; +import { TrustedAppsList } from './trusted_apps_list'; +import { exceptionsListAllHttpMocks } from '../../mocks/exceptions_list_http_mocks'; +import { SEARCHABLE_FIELDS } from '../constants'; +import { parseQueryFilterToKQL } from '../../../common/utils'; +import { useUserPrivileges as _useUserPrivileges } from '../../../../common/components/user_privileges'; + +jest.mock('../../../../common/components/user_privileges'); + +describe('When on the trusted applications page', () => { + let render: () => ReturnType; + let renderResult: ReturnType; + let history: AppContextTestRender['history']; + let mockedContext: AppContextTestRender; + let apiMocks: ReturnType; + + beforeEach(() => { + mockedContext = createAppRootMockRenderer(); + ({ history } = mockedContext); + render = () => (renderResult = mockedContext.render()); + + apiMocks = exceptionsListAllHttpMocks(mockedContext.coreStart.http); + + act(() => { + history.push(TRUSTED_APPS_PATH); + }); + }); + + it('should search using expected exception item fields', async () => { + const expectedFilterString = parseQueryFilterToKQL('fooFooFoo', SEARCHABLE_FIELDS); + const { findAllByTestId } = render(); + await waitFor(async () => { + await expect(findAllByTestId('trustedAppsListPage-card')).resolves.toHaveLength(10); + }); + + apiMocks.responseProvider.exceptionsFind.mockClear(); + userEvent.type(renderResult.getByTestId('searchField'), 'fooFooFoo'); + userEvent.click(renderResult.getByTestId('searchButton')); + await waitFor(() => { + expect(apiMocks.responseProvider.exceptionsFind).toHaveBeenCalled(); + }); + + expect(apiMocks.responseProvider.exceptionsFind).toHaveBeenLastCalledWith( + expect.objectContaining({ + query: expect.objectContaining({ + filter: expectedFilterString, + }), + }) + ); + }); +}); diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_list.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_list.tsx new file mode 100644 index 000000000000..359e9d1aeb99 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_list.tsx @@ -0,0 +1,123 @@ +/* + * 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 React, { memo } from 'react'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { DocLinks } from '@kbn/doc-links'; +import { EuiLink } from '@elastic/eui'; + +import { useHttp } from '../../../../common/lib/kibana'; +import { ArtifactListPage, ArtifactListPageProps } from '../../../components/artifact_list_page'; +import { TrustedAppsApiClient } from '../service'; +import { TrustedAppsForm } from './components/form'; +import { SEARCHABLE_FIELDS } from '../constants'; + +const TRUSTED_APPS_PAGE_LABELS: ArtifactListPageProps['labels'] = { + pageTitle: i18n.translate('xpack.securitySolution.trustedApps.pageTitle', { + defaultMessage: 'Trusted applications', + }), + pageAboutInfo: i18n.translate('xpack.securitySolution.trustedApps.pageAboutInfo', { + defaultMessage: + 'Trusted applications improve performance or alleviate conflicts with other applications running on your hosts.', + }), + pageAddButtonTitle: i18n.translate('xpack.securitySolution.trustedApps.pageAddButtonTitle', { + defaultMessage: 'Add trusted application', + }), + getShowingCountLabel: (total) => + i18n.translate('xpack.securitySolution.trustedApps.showingTotal', { + defaultMessage: + 'Showing {total} {total, plural, one {trusted application} other {trusted applications}}', + values: { total }, + }), + cardActionEditLabel: i18n.translate('xpack.securitySolution.trustedApps.cardActionEditLabel', { + defaultMessage: 'Edit trusted application', + }), + cardActionDeleteLabel: i18n.translate( + 'xpack.securitySolution.trustedApps.cardActionDeleteLabel', + { + defaultMessage: 'Delete trusted application', + } + ), + flyoutCreateTitle: i18n.translate('xpack.securitySolution.trustedApps.flyoutCreateTitle', { + defaultMessage: 'Add trusted application', + }), + flyoutEditTitle: i18n.translate('xpack.securitySolution.trustedApps.flyoutEditTitle', { + defaultMessage: 'Edit trusted application', + }), + flyoutCreateSubmitButtonLabel: i18n.translate( + 'xpack.securitySolution.trustedApps.flyoutCreateSubmitButtonLabel', + { defaultMessage: 'Add trusted application' } + ), + flyoutCreateSubmitSuccess: ({ name }) => + i18n.translate('xpack.securitySolution.trustedApps.flyoutCreateSubmitSuccess', { + defaultMessage: '"{name}" has been added to your trusted applications.', + values: { name }, + }), + flyoutEditSubmitSuccess: ({ name }) => + i18n.translate('xpack.securitySolution.trustedApps.flyoutEditSubmitSuccess', { + defaultMessage: '"{name}" has been updated.', + values: { name }, + }), + flyoutDowngradedLicenseDocsInfo: ( + securitySolutionDocsLinks: DocLinks['securitySolution'] + ): React.ReactNode => { + return ( + <> + + + + + + ); + }, + deleteActionSuccess: (itemName) => + i18n.translate('xpack.securitySolution.trustedApps.deleteSuccess', { + defaultMessage: '"{itemName}" has been removed from trusted applications.', + values: { itemName }, + }), + emptyStateTitle: i18n.translate('xpack.securitySolution.trustedApps.emptyStateTitle', { + defaultMessage: 'Add your first trusted application', + }), + emptyStateInfo: i18n.translate('xpack.securitySolution.trustedApps.emptyStateInfo', { + defaultMessage: + 'Add a trusted application to improve performance or alleviate conflicts with other applications running on your hosts.', + }), + emptyStatePrimaryButtonLabel: i18n.translate( + 'xpack.securitySolution.trustedApps.emptyStatePrimaryButtonLabel', + { defaultMessage: 'Add trusted application' } + ), + searchPlaceholderInfo: i18n.translate( + 'xpack.securitySolution.trustedApps.searchPlaceholderInfo', + { + defaultMessage: 'Search on the fields below: name, description, value', + } + ), +}; + +export const TrustedAppsList = memo(() => { + const http = useHttp(); + const trustedAppsApiClient = TrustedAppsApiClient.getInstance(http); + + return ( + + ); +}); + +TrustedAppsList.displayName = 'TrustedAppsList'; diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_notifications.test.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_notifications.test.tsx deleted file mode 100644 index ebd1e06abdc4..000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_notifications.test.tsx +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { Provider } from 'react-redux'; -import { render } from '@testing-library/react'; - -import { NotificationsStart } from '@kbn/core/public'; - -import { coreMock } from '@kbn/core/public/mocks'; -import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public/context'; - -import { - createGlobalNoMiddlewareStore, - createSampleTrustedApp, - createServerApiError, -} from '../test_utils'; - -import { TrustedAppsNotifications } from './trusted_apps_notifications'; - -const mockNotifications = () => coreMock.createStart({ basePath: '/mock' }).notifications; - -const renderNotifications = ( - store: ReturnType, - notifications: NotificationsStart -) => { - const Wrapper: React.FC = ({ children }) => ( - - {children} - - ); - - return render(, { wrapper: Wrapper }); -}; - -describe('TrustedAppsNotifications', () => { - it('renders correctly initially', () => { - const notifications = mockNotifications(); - - renderNotifications(createGlobalNoMiddlewareStore(), notifications); - - expect(notifications.toasts.addSuccess).not.toBeCalled(); - expect(notifications.toasts.addDanger).not.toBeCalled(); - }); - - it('shows success notification when deletion successful', () => { - const store = createGlobalNoMiddlewareStore(); - const notifications = mockNotifications(); - - renderNotifications(store, notifications); - - store.dispatch({ - type: 'trustedAppDeletionDialogStarted', - payload: { entry: createSampleTrustedApp(3) }, - }); - store.dispatch({ - type: 'trustedAppDeletionSubmissionResourceStateChanged', - payload: { newState: { type: 'LoadedResourceState', data: null } }, - }); - store.dispatch({ - type: 'trustedAppDeletionDialogClosed', - }); - - expect(notifications.toasts.addSuccess).toBeCalledWith({ - text: '"trusted app 3" has been removed from the trusted applications list.', - title: 'Successfully removed', - }); - expect(notifications.toasts.addDanger).not.toBeCalled(); - }); - - it('shows error notification when deletion fails', () => { - const store = createGlobalNoMiddlewareStore(); - const notifications = mockNotifications(); - - renderNotifications(store, notifications); - - store.dispatch({ - type: 'trustedAppDeletionDialogStarted', - payload: { entry: createSampleTrustedApp(3) }, - }); - store.dispatch({ - type: 'trustedAppDeletionSubmissionResourceStateChanged', - payload: { - newState: { type: 'FailedResourceState', error: createServerApiError('Not Found') }, - }, - }); - - expect(notifications.toasts.addSuccess).not.toBeCalled(); - expect(notifications.toasts.addDanger).toBeCalledWith({ - text: 'Unable to remove "trusted app 3" from the trusted applications list. Reason: Not Found', - title: 'Removal failure', - }); - }); -}); diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_notifications.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_notifications.tsx deleted file mode 100644 index dd03636d7cc6..000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_notifications.tsx +++ /dev/null @@ -1,115 +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 React, { memo, useState } from 'react'; -import { i18n } from '@kbn/i18n'; - -import { ServerApiError } from '../../../../common/types'; -import { Immutable, NewTrustedApp, TrustedApp } from '../../../../../common/endpoint/types'; -import { - getCreationDialogFormEntry, - getDeletionDialogEntry, - getDeletionError, - isCreationSuccessful, - isDeletionSuccessful, - isEdit, -} from '../store/selectors'; - -import { useToasts } from '../../../../common/lib/kibana'; -import { useTrustedAppsSelector } from './hooks'; - -const getDeletionErrorMessage = (error: ServerApiError, entry: Immutable) => { - return { - title: i18n.translate('xpack.securitySolution.trustedapps.deletionError.title', { - defaultMessage: 'Removal failure', - }), - text: i18n.translate('xpack.securitySolution.trustedapps.deletionError.text', { - defaultMessage: - 'Unable to remove "{name}" from the trusted applications list. Reason: {message}', - values: { name: entry.name, message: error.message }, - }), - }; -}; - -const getDeletionSuccessMessage = (entry: Immutable) => { - return { - title: i18n.translate('xpack.securitySolution.trustedapps.deletionSuccess.title', { - defaultMessage: 'Successfully removed', - }), - text: i18n.translate('xpack.securitySolution.trustedapps.deletionSuccess.text', { - defaultMessage: '"{name}" has been removed from the trusted applications list.', - values: { name: entry?.name }, - }), - }; -}; - -const getCreationSuccessMessage = (entry: Immutable) => { - return { - title: i18n.translate('xpack.securitySolution.trustedapps.creationSuccess.title', { - defaultMessage: 'Success!', - }), - text: i18n.translate( - 'xpack.securitySolution.trustedapps.createTrustedAppFlyout.successToastTitle', - { - defaultMessage: '"{name}" has been added to the trusted applications list.', - values: { name: entry.name }, - } - ), - }; -}; - -const getUpdateSuccessMessage = (entry: Immutable) => { - return { - title: i18n.translate('xpack.securitySolution.trustedapps.updateSuccess.title', { - defaultMessage: 'Success!', - }), - text: i18n.translate( - 'xpack.securitySolution.trustedapps.createTrustedAppFlyout.updateSuccessToastTitle', - { - defaultMessage: '"{name}" has been updated.', - values: { name: entry.name }, - } - ), - }; -}; - -export const TrustedAppsNotifications = memo(() => { - const deletionError = useTrustedAppsSelector(getDeletionError); - const deletionDialogEntry = useTrustedAppsSelector(getDeletionDialogEntry); - const deletionSuccessful = useTrustedAppsSelector(isDeletionSuccessful); - const creationDialogNewEntry = useTrustedAppsSelector(getCreationDialogFormEntry); - const creationSuccessful = useTrustedAppsSelector(isCreationSuccessful); - const editMode = useTrustedAppsSelector(isEdit); - const toasts = useToasts(); - - const [wasAlreadyHandled] = useState(new WeakSet()); - - if (deletionError && deletionDialogEntry) { - toasts.addDanger(getDeletionErrorMessage(deletionError, deletionDialogEntry)); - } - - if (deletionSuccessful && deletionDialogEntry) { - toasts.addSuccess(getDeletionSuccessMessage(deletionDialogEntry)); - } - - if ( - creationSuccessful && - creationDialogNewEntry && - !wasAlreadyHandled.has(creationDialogNewEntry) - ) { - wasAlreadyHandled.add(creationDialogNewEntry); - - toasts.addSuccess( - (editMode && getUpdateSuccessMessage(creationDialogNewEntry)) || - getCreationSuccessMessage(creationDialogNewEntry) - ); - } - - return <>; -}); - -TrustedAppsNotifications.displayName = 'TrustedAppsNotifications'; diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.test.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.test.tsx deleted file mode 100644 index d3bf8fe6ed46..000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.test.tsx +++ /dev/null @@ -1,885 +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 React from 'react'; -import * as reactTestingLibrary from '@testing-library/react'; -import { TrustedAppsPage } from './trusted_apps_page'; -import { AppContextTestRender, createAppRootMockRenderer } from '../../../../common/mock/endpoint'; -import { fireEvent } from '@testing-library/dom'; -import { MiddlewareActionSpyHelper } from '../../../../common/store/test_utils'; -import { ConditionEntryField, OperatingSystem } from '@kbn/securitysolution-utils'; -import { TrustedApp } from '../../../../../common/endpoint/types'; -import { HttpFetchOptions, HttpFetchOptionsWithPath } from '@kbn/core/public'; -import { isFailedResourceState, isLoadedResourceState } from '../state'; -import { forceHTMLElementOffsetWidth } from '../../../components/effected_policy_select/test_utils'; -import { toUpdateTrustedApp } from '../../../../../common/endpoint/service/trusted_apps/to_update_trusted_app'; -import { licenseService } from '../../../../common/hooks/use_license'; -import { FoundExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; -import { EXCEPTION_LIST_ITEM_URL } from '@kbn/securitysolution-list-constants'; -import { trustedAppsAllHttpMocks } from '../../mocks'; -import { waitFor } from '@testing-library/react'; - -jest.mock('../../../../common/hooks/use_license', () => { - const licenseServiceInstance = { - isPlatinumPlus: jest.fn(), - }; - return { - licenseService: licenseServiceInstance, - useLicense: () => { - return licenseServiceInstance; - }, - }; -}); - -jest.mock('../../../../common/components/user_privileges/endpoint/use_endpoint_privileges'); - -describe('When on the Trusted Apps Page', () => { - const expectedAboutInfo = - 'Add a trusted application to improve performance or alleviate conflicts with other ' + - 'applications running on your hosts.'; - - let mockedContext: AppContextTestRender; - let history: AppContextTestRender['history']; - let coreStart: AppContextTestRender['coreStart']; - let waitForAction: MiddlewareActionSpyHelper['waitForAction']; - let render: () => ReturnType; - let renderResult: ReturnType; - let mockedApis: ReturnType; - let getFakeTrustedApp = jest.fn(); - - const originalScrollTo = window.scrollTo; - const act = reactTestingLibrary.act; - const waitForListUI = async (): Promise => { - await waitFor(() => { - expect(renderResult.getByTestId('trustedAppsListPageContent')).toBeTruthy(); - }); - }; - - beforeAll(() => { - window.scrollTo = () => {}; - }); - - afterAll(() => { - window.scrollTo = originalScrollTo; - }); - - beforeEach(() => { - mockedContext = createAppRootMockRenderer(); - getFakeTrustedApp = jest.fn( - (): TrustedApp => ({ - id: '2d95bec3-b48f-4db7-9622-a2b061cc031d', - version: 'abc123', - name: 'Generated Exception (3xnng)', - os: OperatingSystem.WINDOWS, - created_at: '2021-01-04T13:55:00.561Z', - created_by: 'me', - updated_at: '2021-01-04T13:55:00.561Z', - updated_by: 'me', - description: 'created by ExceptionListItemGenerator', - effectScope: { type: 'global' }, - entries: [ - { - field: ConditionEntryField.PATH, - value: 'one/two', - operator: 'included', - type: 'match', - }, - ], - }) - ); - - history = mockedContext.history; - coreStart = mockedContext.coreStart; - (licenseService.isPlatinumPlus as jest.Mock).mockReturnValue(true); - waitForAction = mockedContext.middlewareSpy.waitForAction; - mockedApis = trustedAppsAllHttpMocks(coreStart.http); - render = () => (renderResult = mockedContext.render()); - reactTestingLibrary.act(() => { - history.push('/administration/trusted_apps'); - }); - window.scrollTo = jest.fn(); - }); - - afterEach(() => reactTestingLibrary.cleanup()); - - describe('and there are trusted app entries', () => { - const renderWithListData = async () => { - render(); - await act(async () => { - await waitForListUI(); - }); - - return renderResult; - }; - - it('should display subtitle info about trusted apps', async () => { - const { getByTestId } = await renderWithListData(); - expect(getByTestId('header-panel-subtitle').textContent).toEqual(expectedAboutInfo); - }); - - it('should display a Add Trusted App button', async () => { - const { getByTestId } = await renderWithListData(); - const addButton = getByTestId('trustedAppsListAddButton'); - expect(addButton.textContent).toBe('Add trusted application'); - }); - - it('should display the searchExceptions', async () => { - await renderWithListData(); - expect(await renderResult.findByTestId('searchExceptions')).not.toBeNull(); - }); - - describe('and the Grid view is being displayed', () => { - const renderWithListDataAndClickOnEditCard = async () => { - await renderWithListData(); - - await act(async () => { - // The 3rd Trusted app to be rendered will be a policy specific one - (await renderResult.findAllByTestId('trustedAppCard-header-actions-button'))[2].click(); - }); - - act(() => { - fireEvent.click(renderResult.getByTestId('editTrustedAppAction')); - }); - }; - - const renderWithListDataAndClickAddButton = async (): Promise< - ReturnType - > => { - await renderWithListData(); - - act(() => { - const addButton = renderResult.getByTestId('trustedAppsListAddButton'); - fireEvent.click(addButton, { button: 1 }); - }); - - // Wait for the policies to be loaded - await act(async () => { - await waitForAction('trustedAppsPoliciesStateChanged', { - validate: (action) => { - return isLoadedResourceState(action.payload); - }, - }); - }); - - return renderResult; - }; - - describe('the license is downgraded to gold or below and the user is editing a per policy TA', () => { - beforeEach(async () => { - (licenseService.isPlatinumPlus as jest.Mock).mockReturnValue(false); - - const originalFakeTrustedAppProvider = getFakeTrustedApp.getMockImplementation(); - getFakeTrustedApp.mockImplementation(() => { - return { - ...originalFakeTrustedAppProvider!(), - effectScope: { - type: 'policy', - policies: ['abc123'], - }, - }; - }); - await renderWithListDataAndClickOnEditCard(); - }); - - it('shows a message at the top of the flyout to inform the user their license is expired', () => { - expect( - renderResult.queryByTestId('addTrustedAppFlyout-expired-license-callout') - ).toBeTruthy(); - }); - }); - - describe('the license is downgraded to gold or below and the user is adding a new TA', () => { - beforeEach(async () => { - (licenseService.isPlatinumPlus as jest.Mock).mockReturnValue(false); - - const originalFakeTrustedAppProvider = getFakeTrustedApp.getMockImplementation(); - getFakeTrustedApp.mockImplementation(() => { - return { - ...originalFakeTrustedAppProvider!(), - effectScope: { - type: 'policy', - policies: ['abc123'], - }, - }; - }); - await renderWithListDataAndClickAddButton(); - }); - it('does not show the expired license message at the top of the flyout', async () => { - expect( - renderResult.queryByTestId('addTrustedAppFlyout-expired-license-callout') - ).toBeNull(); - }); - }); - - describe('and the edit trusted app button is clicked', () => { - beforeEach(async () => { - await renderWithListDataAndClickOnEditCard(); - }); - - it('should persist edit params to url', () => { - expect(history.location.search).toEqual( - '?show=edit&id=bec3b48f-ddb7-4622-a2b0-61cc031d17eb' - ); - }); - - it('should display the Edit flyout', () => { - expect(renderResult.getByTestId('addTrustedAppFlyout')); - }); - - it('should NOT display the about info for trusted apps', () => { - expect(renderResult.queryByTestId('addTrustedAppFlyout-about')).toBeNull(); - }); - - it('should show correct flyout title', () => { - expect(renderResult.getByTestId('addTrustedAppFlyout-headerTitle').textContent).toBe( - 'Edit trusted application' - ); - }); - - it('should display the expected text for the Save button', () => { - expect(renderResult.getByTestId('addTrustedAppFlyout-createButton').textContent).toEqual( - 'Save' - ); - }); - - it('should display trusted app data for edit', async () => { - const formNameInput = renderResult.getByTestId( - 'addTrustedAppFlyout-createForm-nameTextField' - ) as HTMLInputElement; - const formDescriptionInput = renderResult.getByTestId( - 'addTrustedAppFlyout-createForm-descriptionField' - ) as HTMLTextAreaElement; - - expect(formNameInput.value).toEqual('Generated Exception (nng74)'); - expect(formDescriptionInput.value).toEqual('created by ExceptionListItemGenerator'); - }); - - describe('and when Save is clicked', () => { - it('should call the correct api (PUT)', async () => { - await act(async () => { - fireEvent.click(renderResult.getByTestId('addTrustedAppFlyout-createButton')); - await waitForAction('trustedAppCreationSubmissionResourceStateChanged', { - validate({ payload }) { - return isLoadedResourceState(payload.newState); - }, - }); - }); - - expect(coreStart.http.put).toHaveBeenCalledTimes(1); - - const lastCallToPut = coreStart.http.put.mock.calls[0] as unknown as [ - string, - HttpFetchOptions - ]; - - expect(lastCallToPut[0]).toEqual('/api/exception_lists/items'); - - expect(JSON.parse(lastCallToPut[1].body as string)).toEqual({ - _version: '9zawi', - name: 'Generated Exception (nng74)', - description: 'created by ExceptionListItemGenerator', - entries: [ - { - field: 'process.hash.md5', - operator: 'included', - type: 'match', - value: '741462ab431a22233c787baab9b653c7', - }, - { - field: 'process.executable.caseless', - operator: 'included', - type: 'match', - value: 'c:\\fol\\bin.exe', - }, - ], - os_types: ['windows'], - tags: [ - 'policy:ddf6570b-9175-4a6d-b288-61a09771c647', - 'policy:b8e616ae-44fc-4be7-846c-ce8fa5c082dd', - ], - id: '05b5e350-0cad-4dc3-a61d-6e6796b0af39', - comments: [], - item_id: 'bec3b48f-ddb7-4622-a2b0-61cc031d17eb', - namespace_type: 'agnostic', - type: 'simple', - }); - }); - }); - }); - - describe('and attempting to show Edit panel based on URL params', () => { - const renderAndWaitForGetApi = async () => { - // the store action watcher is setup prior to render because `renderWithListData()` - // also awaits API calls and this action could be missed. - const apiResponseForEditTrustedApp = waitForAction( - 'trustedAppCreationEditItemStateChanged', - { - validate({ payload }) { - return isLoadedResourceState(payload) || isFailedResourceState(payload); - }, - } - ); - - await renderWithListData(); - - await reactTestingLibrary.act(async () => { - await apiResponseForEditTrustedApp; - }); - - return renderResult; - }; - - beforeEach(() => { - reactTestingLibrary.act(() => { - history.push('/administration/trusted_apps?show=edit&id=9999-edit-8888'); - }); - }); - - it('should retrieve trusted app via API using url `id`', async () => { - await renderAndWaitForGetApi(); - - expect(coreStart.http.get.mock.calls).toContainEqual([ - EXCEPTION_LIST_ITEM_URL, - { - query: { - item_id: '9999-edit-8888', - namespace_type: 'agnostic', - }, - }, - ]); - - expect( - ( - renderResult.getByTestId( - 'addTrustedAppFlyout-createForm-nameTextField' - ) as HTMLInputElement - ).value - ).toEqual('Generated Exception (u6kh2)'); - }); - - it('should redirect to list and show toast message if `id` is missing from URL', async () => { - reactTestingLibrary.act(() => { - history.push('/administration/trusted_apps?show=edit&id='); - }); - - await renderAndWaitForGetApi(); - - expect(history.location.search).toEqual(''); - expect(coreStart.notifications.toasts.addWarning.mock.calls[0][0]).toEqual( - 'Unable to edit trusted application (No id provided)' - ); - }); - - it('should redirect to list and show toast message on API error for GET of `id`', async () => { - // Mock the API GET for the trusted application - mockedApis.responseProvider.trustedApp.mockImplementation(() => { - throw new Error('test: api error response'); - }); - - await renderAndWaitForGetApi(); - - expect(history.location.search).toEqual(''); - expect(coreStart.notifications.toasts.addWarning.mock.calls[0][0]).toEqual( - 'Unable to edit trusted application (test: api error response)' - ); - }); - }); - }); - }); - - describe('and the Add Trusted App button is clicked', () => { - const renderAndClickAddButton = async (): Promise< - ReturnType - > => { - render(); - await act(async () => { - await Promise.all([ - waitForAction('trustedAppsListResourceStateChanged'), - waitForAction('trustedAppsExistStateChanged', { - validate({ payload }) { - return isLoadedResourceState(payload); - }, - }), - ]); - }); - - act(() => { - const addButton = renderResult.getByTestId('trustedAppsListAddButton'); - fireEvent.click(addButton, { button: 1 }); - }); - - // Wait for the policies to be loaded - await act(async () => { - await waitForAction('trustedAppsPoliciesStateChanged', { - validate: (action) => { - return isLoadedResourceState(action.payload); - }, - }); - }); - - return renderResult; - }; - - it('should display the create flyout', async () => { - const { getByTestId } = await renderAndClickAddButton(); - const flyout = getByTestId('addTrustedAppFlyout'); - expect(flyout).not.toBeNull(); - - const flyoutTitle = getByTestId('addTrustedAppFlyout-headerTitle'); - expect(flyoutTitle.textContent).toBe('Add trusted application'); - - expect(getByTestId('addTrustedAppFlyout-about')); - }); - - it('should update the URL to indicate the flyout is opened', async () => { - await renderAndClickAddButton(); - expect(/show\=create/.test(history.location.search)).toBe(true); - }); - - it('should preserve other URL search params', async () => { - const createListResponse = - mockedApis.responseProvider.trustedAppsList.getMockImplementation()!; - mockedApis.responseProvider.trustedAppsList.mockImplementation((...args) => { - const response = createListResponse(...args); - response.total = 100; // Trigger the UI to show pagination - return response; - }); - - reactTestingLibrary.act(() => { - history.push('/administration/trusted_apps?page_index=2&page_size=20'); - }); - await renderAndClickAddButton(); - expect(history.location.search).toBe('?page_index=2&page_size=20&show=create'); - }); - - it('should display create form', async () => { - const { queryByTestId } = await renderAndClickAddButton(); - expect(queryByTestId('addTrustedAppFlyout-createForm')).not.toBeNull(); - }); - - it('should have list of policies populated', async () => { - const resetEnv = forceHTMLElementOffsetWidth(); - await renderAndClickAddButton(); - act(() => { - fireEvent.click(renderResult.getByTestId('perPolicy')); - }); - expect(renderResult.getByTestId('policy-ddf6570b-9175-4a6d-b288-61a09771c647')); - resetEnv(); - }); - - it('should initially have the flyout Add button disabled', async () => { - const { getByTestId } = await renderAndClickAddButton(); - expect((getByTestId('addTrustedAppFlyout-createButton') as HTMLButtonElement).disabled).toBe( - true - ); - }); - - it('should close flyout if cancel button is clicked', async () => { - const { getByTestId, queryByTestId } = await renderAndClickAddButton(); - const cancelButton = getByTestId('addTrustedAppFlyout-cancelButton'); - await reactTestingLibrary.act(async () => { - fireEvent.click(cancelButton, { button: 1 }); - await waitForAction('trustedAppCreationDialogClosed'); - }); - expect(history.location.search).toBe(''); - expect(queryByTestId('addTrustedAppFlyout')).toBeNull(); - }); - - it('should close flyout if flyout close button is clicked', async () => { - const { getByTestId, queryByTestId } = await renderAndClickAddButton(); - const flyoutCloseButton = getByTestId('euiFlyoutCloseButton'); - await reactTestingLibrary.act(async () => { - fireEvent.click(flyoutCloseButton, { button: 1 }); - await waitForAction('trustedAppCreationDialogClosed'); - }); - expect(queryByTestId('addTrustedAppFlyout')).toBeNull(); - expect(history.location.search).toBe(''); - }); - - describe('and when the form data is valid', () => { - const fillInCreateForm = async () => { - mockedContext.store.dispatch({ - type: 'trustedAppCreationDialogFormStateUpdated', - payload: { - isValid: true, - entry: toUpdateTrustedApp(getFakeTrustedApp()), - }, - }); - }; - - it('should enable the Flyout Add button', async () => { - await renderAndClickAddButton(); - await fillInCreateForm(); - - const flyoutAddButton = renderResult.getByTestId( - 'addTrustedAppFlyout-createButton' - ) as HTMLButtonElement; - - expect(flyoutAddButton.disabled).toBe(false); - }); - - describe('and the Flyout Add button is clicked', () => { - let releasePostCreateApi: () => void; - - beforeEach(async () => { - // Add a delay to the create api response provider and expose a function that allows - // us to release it at the right time. - mockedApis.responseProvider.trustedAppCreate.mockDelay.mockReturnValue( - new Promise((resolve) => { - releasePostCreateApi = resolve as typeof releasePostCreateApi; - }) - ); - - await renderAndClickAddButton(); - await fillInCreateForm(); - - const userClickedSaveActionWatcher = waitForAction('trustedAppCreationDialogConfirmed'); - reactTestingLibrary.act(() => { - fireEvent.click(renderResult.getByTestId('addTrustedAppFlyout-createButton'), { - button: 1, - }); - }); - - await reactTestingLibrary.act(async () => { - await userClickedSaveActionWatcher; - }); - }); - - afterEach(() => releasePostCreateApi()); - - it('should display info about Trusted Apps', async () => { - expect(renderResult.getByTestId('addTrustedAppFlyout-about').textContent).toEqual( - expectedAboutInfo - ); - }); - - it('should disable the Cancel button', async () => { - expect( - (renderResult.getByTestId('addTrustedAppFlyout-cancelButton') as HTMLButtonElement) - .disabled - ).toBe(true); - releasePostCreateApi(); - }); - - it('should hide the dialog close button', async () => { - expect(renderResult.queryByTestId('euiFlyoutCloseButton')).toBeNull(); - }); - - it('should disable the flyout Add button and set it to loading', async () => { - const saveButton = renderResult.getByTestId( - 'addTrustedAppFlyout-createButton' - ) as HTMLButtonElement; - expect(saveButton.disabled).toBe(true); - expect(saveButton.querySelector('.euiLoadingSpinner')).not.toBeNull(); - }); - - describe('and if create was successful', () => { - beforeEach(async () => { - await reactTestingLibrary.act(async () => { - const serverResponseAction = waitForAction( - 'trustedAppCreationSubmissionResourceStateChanged' - ); - - coreStart.http.get.mockClear(); - releasePostCreateApi(); - await serverResponseAction; - }); - }); - - it('should close the flyout', () => { - expect(renderResult.queryByTestId('addTrustedAppFlyout')).toBeNull(); - }); - - it('should show success toast notification', () => { - expect(coreStart.notifications.toasts.addSuccess.mock.calls[0][0]).toEqual({ - text: '"Generated Exception (3xnng)" has been added to the trusted applications list.', - title: 'Success!', - }); - }); - - it('should trigger the List to reload', () => { - const isCalled = coreStart.http.get.mock.calls.some( - (call) => call[0].toString() === `${EXCEPTION_LIST_ITEM_URL}/_find` - ); - expect(isCalled).toEqual(true); - }); - }); - - describe('and if create failed', () => { - const ServerErrorResponseBodyMock = class extends Error { - public readonly body: { message: string }; - constructor(message = 'Test - Bad Call') { - super(message); - this.body = { - message, - }; - } - }; - beforeEach(async () => { - const failedCreateApiResponse = new ServerErrorResponseBodyMock(); - - mockedApis.responseProvider.trustedAppCreate.mockImplementation(() => { - throw failedCreateApiResponse; - }); - - await reactTestingLibrary.act(async () => { - const serverResponseAction = waitForAction( - 'trustedAppCreationSubmissionResourceStateChanged', - { - validate({ payload }) { - return isFailedResourceState(payload.newState); - }, - } - ); - - releasePostCreateApi(); - await serverResponseAction; - }); - }); - - it('should continue to show the flyout', () => { - expect(renderResult.getByTestId('addTrustedAppFlyout')).not.toBeNull(); - }); - - it('should enable the Cancel Button', () => { - expect( - (renderResult.getByTestId('addTrustedAppFlyout-cancelButton') as HTMLButtonElement) - .disabled - ).toBe(false); - }); - - it('should show the dialog close button', () => { - expect(renderResult.getByTestId('euiFlyoutCloseButton')).not.toBeNull(); - }); - - it('should enable the flyout Add button and remove loading indicating', () => { - expect( - (renderResult.getByTestId('addTrustedAppFlyout-createButton') as HTMLButtonElement) - .disabled - ).toBe(false); - }); - - it('should show API errors in the form', () => { - expect(renderResult.container.querySelector('.euiForm__errors')).not.toBeNull(); - }); - }); - }); - }); - - describe('and when the form data is not valid', () => { - it('should not enable the Flyout Add button with an invalid hash', async () => { - await renderAndClickAddButton(); - const { getByTestId } = renderResult; - - reactTestingLibrary.act(() => { - fireEvent.change(getByTestId('addTrustedAppFlyout-createForm-nameTextField'), { - target: { value: 'trusted app A' }, - }); - - fireEvent.change( - getByTestId('addTrustedAppFlyout-createForm-conditionsBuilder-group1-entry0-value'), - { target: { value: 'invalid hash' } } - ); - }); - - const flyoutAddButton = getByTestId( - 'addTrustedAppFlyout-createButton' - ) as HTMLButtonElement; - expect(flyoutAddButton.disabled).toBe(true); - }); - }); - }); - - describe('and there are no trusted apps', () => { - const releaseExistsResponse = jest.fn((): FoundExceptionListItemSchema => { - return { - data: [], - total: 0, - page: 1, - per_page: 1, - }; - }); - const releaseListResponse = jest.fn((): FoundExceptionListItemSchema => { - return { - data: [], - total: 0, - page: 1, - per_page: 20, - }; - }); - - beforeEach(() => { - mockedApis.responseProvider.trustedAppsList.mockImplementation(({ query }) => { - const { page, per_page: perPage } = query as { page: number; per_page: number }; - - if (page === 1 && perPage === 1) { - return releaseExistsResponse(); - } else { - return releaseListResponse(); - } - }); - }); - - afterEach(() => { - releaseExistsResponse.mockClear(); - releaseListResponse.mockClear(); - }); - - it('should show a loader until trusted apps existence can be confirmed', async () => { - render(); - expect(await renderResult.findByTestId('trustedAppsListLoader')).not.toBeNull(); - }); - - it('should show Empty Prompt if not entries exist', async () => { - render(); - await act(async () => { - await waitForAction('trustedAppsExistStateChanged'); - }); - expect(await renderResult.findByTestId('trustedAppEmptyState')).not.toBeNull(); - }); - - it('should hide empty prompt and show list after one trusted app is added', async () => { - render(); - await act(async () => { - await waitForAction('trustedAppsExistStateChanged'); - }); - expect(await renderResult.findByTestId('trustedAppEmptyState')).not.toBeNull(); - releaseListResponse.mockReturnValueOnce({ - data: [mockedApis.responseProvider.trustedApp({ query: {} } as HttpFetchOptionsWithPath)], - total: 1, - page: 1, - per_page: 20, - }); - releaseExistsResponse.mockReturnValueOnce({ - data: [mockedApis.responseProvider.trustedApp({ query: {} } as HttpFetchOptionsWithPath)], - total: 1, - page: 1, - per_page: 1, - }); - - await act(async () => { - mockedContext.store.dispatch({ - type: 'trustedAppsListDataOutdated', - }); - await waitForAction('trustedAppsListResourceStateChanged'); - }); - - expect(await renderResult.findByTestId('trustedAppsListPageContent')).not.toBeNull(); - }); - - it('should should show empty prompt once the last trusted app entry is deleted', async () => { - releaseListResponse.mockReturnValueOnce({ - data: [mockedApis.responseProvider.trustedApp({ query: {} } as HttpFetchOptionsWithPath)], - total: 1, - page: 1, - per_page: 20, - }); - releaseExistsResponse.mockReturnValueOnce({ - data: [mockedApis.responseProvider.trustedApp({ query: {} } as HttpFetchOptionsWithPath)], - total: 1, - page: 1, - per_page: 1, - }); - - render(); - - await act(async () => { - await waitForAction('trustedAppsExistStateChanged'); - }); - - expect(await renderResult.findByTestId('trustedAppsListPageContent')).not.toBeNull(); - - await act(async () => { - mockedContext.store.dispatch({ - type: 'trustedAppsListDataOutdated', - }); - await waitForAction('trustedAppsListResourceStateChanged'); - }); - - expect(await renderResult.findByTestId('trustedAppEmptyState')).not.toBeNull(); - }); - - it('should not display the searchExceptions', async () => { - render(); - await act(async () => { - await waitForAction('trustedAppsExistStateChanged'); - }); - expect(renderResult.queryByTestId('searchExceptions')).toBeNull(); - }); - }); - - describe('and the search is dispatched', () => { - beforeEach(async () => { - reactTestingLibrary.act(() => { - history.push('/administration/trusted_apps?filter=test'); - }); - render(); - await act(async () => { - await waitForListUI(); - }); - }); - - it('search bar is filled with query params', () => { - expect(renderResult.getByDisplayValue('test')).not.toBeNull(); - }); - - it('search action is dispatched', async () => { - await act(async () => { - fireEvent.click(renderResult.getByTestId('searchButton')); - expect(await waitForAction('userChangedUrl')).not.toBeNull(); - }); - }); - }); - - describe('and the back button is present', () => { - beforeEach(async () => { - render(); - await act(async () => { - await waitForListUI(); - }); - reactTestingLibrary.act(() => { - history.push('/administration/trusted_apps', { - onBackButtonNavigateTo: [{ appId: 'appId' }], - backButtonLabel: 'back to fleet', - backButtonUrl: '/fleet', - }); - }); - }); - - it('back button is present', () => { - const button = renderResult.queryByTestId('backToOrigin'); - expect(button).not.toBeNull(); - expect(button).toHaveAttribute('href', '/fleet'); - }); - - it('back button is present after push history', () => { - reactTestingLibrary.act(() => { - history.push('/administration/trusted_apps'); - }); - const button = renderResult.queryByTestId('backToOrigin'); - expect(button).not.toBeNull(); - expect(button).toHaveAttribute('href', '/fleet'); - }); - }); - - describe('and the back button is not present', () => { - beforeEach(async () => { - render(); - await act(async () => { - await waitForAction('trustedAppsListResourceStateChanged'); - }); - reactTestingLibrary.act(() => { - history.push('/administration/trusted_apps'); - }); - }); - - it('back button is not present when missing history params', () => { - const button = renderResult.queryByTestId('backToOrigin'); - expect(button).toBeNull(); - }); - }); -}); diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.tsx deleted file mode 100644 index 6b3f59f44ce1..000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.tsx +++ /dev/null @@ -1,188 +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 React, { memo, useMemo, useCallback } from 'react'; -import { useDispatch } from 'react-redux'; -import { Dispatch } from 'redux'; -import { useLocation } from 'react-router-dom'; -import { FormattedMessage } from '@kbn/i18n-react'; -import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiText } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; - -import { - checkingIfEntriesExist, - entriesExist, - getCurrentLocation, - getListTotalItemsCount, - listOfPolicies, - prevEntriesExist, -} from '../store/selectors'; -import { useTrustedAppsNavigateCallback, useTrustedAppsSelector } from './hooks'; -import { AdministrationListPage } from '../../../components/administration_list_page'; -import { CreateTrustedAppFlyout } from './components/create_trusted_app_flyout'; -import { TrustedAppsGrid } from './components/trusted_apps_grid'; -import { TrustedAppDeletionDialog } from './trusted_app_deletion_dialog'; -import { TrustedAppsNotifications } from './trusted_apps_notifications'; -import { AppAction } from '../../../../common/store/actions'; -import { ABOUT_TRUSTED_APPS, SEARCH_TRUSTED_APP_PLACEHOLDER } from './translations'; -import { EmptyState } from './components/empty_state'; -import { SearchExceptions } from '../../../components/search_exceptions'; -import { BackToExternalAppSecondaryButton } from '../../../components/back_to_external_app_secondary_button'; -import { BackToExternalAppButton } from '../../../components/back_to_external_app_button'; -import { ListPageRouteState } from '../../../../../common/endpoint/types'; -import { ManagementPageLoader } from '../../../components/management_page_loader'; -import { useMemoizedRouteState } from '../../../common/hooks'; - -export const TrustedAppsPage = memo(() => { - const dispatch = useDispatch>(); - const { state: routeState } = useLocation(); - const location = useTrustedAppsSelector(getCurrentLocation); - const totalItemsCount = useTrustedAppsSelector(getListTotalItemsCount); - const isCheckingIfEntriesExists = useTrustedAppsSelector(checkingIfEntriesExist); - const policyList = useTrustedAppsSelector(listOfPolicies); - const doEntriesExist = useTrustedAppsSelector(entriesExist); - const didEntriesExist = useTrustedAppsSelector(prevEntriesExist); - const navigationCallbackQuery = useTrustedAppsNavigateCallback( - (query: string, includedPolicies?: string) => ({ - filter: query, - included_policies: includedPolicies, - }) - ); - - const memoizedRouteState = useMemoizedRouteState(routeState); - - const backButtonEmptyComponent = useMemo(() => { - if (memoizedRouteState && memoizedRouteState.onBackButtonNavigateTo) { - return ; - } - }, [memoizedRouteState]); - - const backButtonHeaderComponent = useMemo(() => { - if (memoizedRouteState && memoizedRouteState.onBackButtonNavigateTo) { - return ; - } - }, [memoizedRouteState]); - - const handleAddButtonClick = useTrustedAppsNavigateCallback(() => ({ - show: 'create', - id: undefined, - })); - const handleAddFlyoutClose = useTrustedAppsNavigateCallback(() => ({ - show: undefined, - id: undefined, - })); - - const handleOnSearch = useCallback( - (query: string, includedPolicies?: string) => { - dispatch({ type: 'trustedAppForceRefresh', payload: { forceRefresh: true } }); - navigationCallbackQuery(query, includedPolicies); - }, - [dispatch, navigationCallbackQuery] - ); - - const showCreateFlyout = !!location.show; - - const canDisplayContent = useCallback( - () => doEntriesExist || (isCheckingIfEntriesExists && didEntriesExist), - [didEntriesExist, doEntriesExist, isCheckingIfEntriesExists] - ); - - const addButton = ( - - - - ); - - const content = ( - <> - - - {showCreateFlyout && ( - - )} - - {canDisplayContent() ? ( - <> - - - - - - {i18n.translate('xpack.securitySolution.trustedapps.list.totalCount', { - defaultMessage: - 'Showing {totalItemsCount, plural, one {# trusted application} other {# trusted applications}}', - values: { totalItemsCount }, - })} - - - - - - - - - - ) : ( - - )} - - ); - - return ( - - } - subtitle={ABOUT_TRUSTED_APPS} - actions={addButton} - hideHeader={!canDisplayContent()} - > - - - {isCheckingIfEntriesExists && !didEntriesExist ? ( - - ) : ( - content - )} - - ); -}); - -TrustedAppsPage.displayName = 'TrustedAppsPage'; diff --git a/x-pack/plugins/security_solution/public/management/store/middleware.ts b/x-pack/plugins/security_solution/public/management/store/middleware.ts index d011a9dcb91a..86a5ade34005 100644 --- a/x-pack/plugins/security_solution/public/management/store/middleware.ts +++ b/x-pack/plugins/security_solution/public/management/store/middleware.ts @@ -14,12 +14,10 @@ import { MANAGEMENT_STORE_ENDPOINTS_NAMESPACE, MANAGEMENT_STORE_GLOBAL_NAMESPACE, MANAGEMENT_STORE_POLICY_DETAILS_NAMESPACE, - MANAGEMENT_STORE_TRUSTED_APPS_NAMESPACE, MANAGEMENT_STORE_EVENT_FILTERS_NAMESPACE, } from '../common/constants'; import { policyDetailsMiddlewareFactory } from '../pages/policy/store/policy_details'; import { endpointMiddlewareFactory } from '../pages/endpoint_hosts/store/middleware'; -import { trustedAppsPageMiddlewareFactory } from '../pages/trusted_apps/store/middleware'; import { eventFiltersPageMiddlewareFactory } from '../pages/event_filters/store/middleware'; type ManagementSubStateKey = keyof State[typeof MANAGEMENT_STORE_GLOBAL_NAMESPACE]; @@ -42,10 +40,7 @@ export const managementMiddlewareFactory: SecuritySubPluginMiddlewareFactory = ( createSubStateSelector(MANAGEMENT_STORE_ENDPOINTS_NAMESPACE), endpointMiddlewareFactory(coreStart, depsStart) ), - substateMiddlewareFactory( - createSubStateSelector(MANAGEMENT_STORE_TRUSTED_APPS_NAMESPACE), - trustedAppsPageMiddlewareFactory(coreStart, depsStart) - ), + substateMiddlewareFactory( createSubStateSelector(MANAGEMENT_STORE_EVENT_FILTERS_NAMESPACE), eventFiltersPageMiddlewareFactory(coreStart, depsStart) diff --git a/x-pack/plugins/security_solution/public/management/store/reducer.ts b/x-pack/plugins/security_solution/public/management/store/reducer.ts index 662d2b4322bc..2fd20129ddca 100644 --- a/x-pack/plugins/security_solution/public/management/store/reducer.ts +++ b/x-pack/plugins/security_solution/public/management/store/reducer.ts @@ -13,15 +13,12 @@ import { import { MANAGEMENT_STORE_ENDPOINTS_NAMESPACE, MANAGEMENT_STORE_POLICY_DETAILS_NAMESPACE, - MANAGEMENT_STORE_TRUSTED_APPS_NAMESPACE, MANAGEMENT_STORE_EVENT_FILTERS_NAMESPACE, } from '../common/constants'; import { ImmutableCombineReducers } from '../../common/store'; import { Immutable } from '../../../common/endpoint/types'; import { ManagementState } from '../types'; import { endpointListReducer } from '../pages/endpoint_hosts/store/reducer'; -import { initialTrustedAppsPageState } from '../pages/trusted_apps/store/builders'; -import { trustedAppsPageReducer } from '../pages/trusted_apps/store/reducer'; import { initialEventFiltersPageState } from '../pages/event_filters/store/builders'; import { eventFiltersPageReducer } from '../pages/event_filters/store/reducer'; import { initialEndpointPageState } from '../pages/endpoint_hosts/store/builders'; @@ -34,7 +31,6 @@ const immutableCombineReducers: ImmutableCombineReducers = combineReducers; export const mockManagementState: Immutable = { [MANAGEMENT_STORE_POLICY_DETAILS_NAMESPACE]: initialPolicyDetailsState(), [MANAGEMENT_STORE_ENDPOINTS_NAMESPACE]: initialEndpointPageState(), - [MANAGEMENT_STORE_TRUSTED_APPS_NAMESPACE]: initialTrustedAppsPageState(), [MANAGEMENT_STORE_EVENT_FILTERS_NAMESPACE]: initialEventFiltersPageState(), }; @@ -44,6 +40,5 @@ export const mockManagementState: Immutable = { export const managementReducer = immutableCombineReducers({ [MANAGEMENT_STORE_POLICY_DETAILS_NAMESPACE]: policyDetailsReducer, [MANAGEMENT_STORE_ENDPOINTS_NAMESPACE]: endpointListReducer, - [MANAGEMENT_STORE_TRUSTED_APPS_NAMESPACE]: trustedAppsPageReducer, [MANAGEMENT_STORE_EVENT_FILTERS_NAMESPACE]: eventFiltersPageReducer, }); diff --git a/x-pack/plugins/security_solution/public/management/types.ts b/x-pack/plugins/security_solution/public/management/types.ts index 65063fce96e6..0ad0f2e757c0 100644 --- a/x-pack/plugins/security_solution/public/management/types.ts +++ b/x-pack/plugins/security_solution/public/management/types.ts @@ -9,7 +9,6 @@ import { CombinedState } from 'redux'; import { SecurityPageName } from '../app/types'; import { PolicyDetailsState } from './pages/policy/types'; import { EndpointState } from './pages/endpoint_hosts/types'; -import { TrustedAppsListPageState } from './pages/trusted_apps/state'; import { EventFiltersListPageState } from './pages/event_filters/types'; /** @@ -21,7 +20,6 @@ export type ManagementStoreGlobalNamespace = 'management'; export type ManagementState = CombinedState<{ policyDetails: PolicyDetailsState; endpoints: EndpointState; - trustedApps: TrustedAppsListPageState; eventFilters: EventFiltersListPageState; }>; diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/alerts_by_status/alerts_by_status.test.tsx b/x-pack/plugins/security_solution/public/overview/components/detection_response/alerts_by_status/alerts_by_status.test.tsx new file mode 100644 index 000000000000..4c8280f90304 --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/alerts_by_status/alerts_by_status.test.tsx @@ -0,0 +1,122 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; +import { render } from '@testing-library/react'; +import { AlertsByStatus } from './alerts_by_status'; +import { parsedMockAlertsData } from './mock_data'; +import { useKibana } from '../../../../common/lib/kibana/kibana_react'; +import { mockCasesContract } from '@kbn/cases-plugin/public/mocks'; +import { CASES_FEATURE_ID } from '../../../../../common/constants'; +import { TestProviders } from '../../../../common/mock/test_providers'; +import { useAlertsByStatus } from './use_alerts_by_status'; + +jest.mock('../../../../common/lib/kibana/kibana_react'); + +jest.mock('./chart_label', () => { + return { + ChartLabel: jest.fn((props) => ), + }; +}); +jest.mock('./use_alerts_by_status', () => ({ + useAlertsByStatus: jest.fn().mockReturnValue({ + items: [], + isLoading: true, + }), +})); +describe('AlertsByStatus', () => { + const mockCases = mockCasesContract(); + + const props = { + showInspectButton: true, + signalIndexName: 'mock-signal-index', + }; + + beforeEach(() => { + jest.clearAllMocks(); + (useKibana as jest.Mock).mockReturnValue({ + services: { + cases: mockCases, + application: { + capabilities: { [CASES_FEATURE_ID]: { crud_cases: true, read_cases: true } }, + getUrlForApp: jest.fn(), + }, + theme: {}, + }, + }); + (useAlertsByStatus as jest.Mock).mockReturnValue({ + items: [], + isLoading: true, + }); + }); + + test('render HoverVisibilityContainer', () => { + const { container } = render( + + + + ); + expect( + container.querySelector(`[data-test-subj="hoverVisibilityContainer"]`) + ).toBeInTheDocument(); + }); + test('render HistogramPanel', () => { + const { container } = render( + + + + ); + expect( + container.querySelector(`[data-test-subj="detection-response-alerts-by-status-panel"]`) + ).toBeInTheDocument(); + }); + + test('render HeaderSection', () => { + const { container } = render( + + + + ); + expect(container.querySelector(`[data-test-subj="header-section"]`)).toBeInTheDocument(); + }); + + test('render Legend', () => { + const testProps = { + ...props, + isInitialLoading: false, + }; + (useAlertsByStatus as jest.Mock).mockReturnValue({ + items: parsedMockAlertsData, + isLoading: false, + }); + + const { container } = render( + + + + ); + expect(container.querySelector(`[data-test-subj="legend"]`)).toBeInTheDocument(); + }); + + test('render toggle query button', () => { + const testProps = { + ...props, + isInitialLoading: false, + }; + + (useAlertsByStatus as jest.Mock).mockReturnValue({ + items: parsedMockAlertsData, + isLoading: false, + }); + + const { container } = render( + + + + ); + expect(container.querySelector(`[data-test-subj="query-toggle-header"]`)).toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/alerts_by_status/alerts_by_status.tsx b/x-pack/plugins/security_solution/public/overview/components/detection_response/alerts_by_status/alerts_by_status.tsx new file mode 100644 index 000000000000..963b12a35dbf --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/alerts_by_status/alerts_by_status.tsx @@ -0,0 +1,214 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiProgress, EuiSpacer, EuiText } from '@elastic/eui'; +import React, { useCallback, useMemo } from 'react'; +import { ShapeTreeNode } from '@elastic/charts'; +import { Severity } from '@kbn/securitysolution-io-ts-alerting-types'; +import styled from 'styled-components'; +import { DonutChart, FillColor } from '../../../../common/components/charts/donutchart'; +import { SecurityPageName } from '../../../../../common/constants'; +import { useNavigation } from '../../../../common/lib/kibana'; +import { HeaderSection } from '../../../../common/components/header_section'; +import { HoverVisibilityContainer } from '../../../../common/components/hover_visibility_container'; +import { BUTTON_CLASS as INPECT_BUTTON_CLASS } from '../../../../common/components/inspect'; +import { LegendItem } from '../../../../common/components/charts/legend_item'; +import { useAlertsByStatus } from './use_alerts_by_status'; +import { + ALERTS, + ALERTS_TITLE, + STATUS_ACKNOWLEDGED, + STATUS_CLOSED, + STATUS_CRITICAL_LABEL, + STATUS_HIGH_LABEL, + STATUS_LOW_LABEL, + STATUS_MEDIUM_LABEL, + STATUS_OPEN, +} from '../translations'; +import { useQueryToggle } from '../../../../common/containers/query_toggle'; +import { getDetectionEngineUrl, useFormatUrl } from '../../../../common/components/link_to'; +import { VIEW_ALERTS } from '../../../pages/translations'; +import { LastUpdatedAt, SEVERITY_COLOR } from '../utils'; +import { FormattedCount } from '../../../../common/components/formatted_number'; +import { ChartLabel } from './chart_label'; +import { Legend } from '../../../../common/components/charts/legend'; +import { emptyDonutColor } from '../../../../common/components/charts/donutchart_empty'; +import { LinkButton } from '../../../../common/components/links'; + +const donutHeight = 120; +const StyledFlexItem = styled(EuiFlexItem)` + padding: 0 4px; +`; + +const StyledLegendFlexItem = styled(EuiFlexItem)` + padding-left: 32px; + padding-top: 45px; +`; + +interface AlertsByStatusProps { + signalIndexName: string | null; +} + +const legendField = 'kibana.alert.severity'; +const chartConfigs: Array<{ key: Severity; label: string; color: string }> = [ + { key: 'critical', label: STATUS_CRITICAL_LABEL, color: SEVERITY_COLOR.critical }, + { key: 'high', label: STATUS_HIGH_LABEL, color: SEVERITY_COLOR.high }, + { key: 'medium', label: STATUS_MEDIUM_LABEL, color: SEVERITY_COLOR.medium }, + { key: 'low', label: STATUS_LOW_LABEL, color: SEVERITY_COLOR.low }, +]; +const DETECTION_RESPONSE_ALERTS_BY_STATUS_ID = 'detection-response-alerts-by-status'; + +export const AlertsByStatus = ({ signalIndexName }: AlertsByStatusProps) => { + const { toggleStatus, setToggleStatus } = useQueryToggle(DETECTION_RESPONSE_ALERTS_BY_STATUS_ID); + const { formatUrl, search: urlSearch } = useFormatUrl(SecurityPageName.alerts); + const { navigateTo } = useNavigation(); + const goToAlerts = useCallback( + (ev) => { + ev.preventDefault(); + navigateTo({ + deepLinkId: SecurityPageName.alerts, + path: getDetectionEngineUrl(urlSearch), + }); + }, + [navigateTo, urlSearch] + ); + + const detailsButtonOptions = useMemo( + () => ({ + name: VIEW_ALERTS, + href: formatUrl(getDetectionEngineUrl()), + onClick: goToAlerts, + }), + [formatUrl, goToAlerts] + ); + + const { + items: donutData, + isLoading: loading, + updatedAt, + } = useAlertsByStatus({ + signalIndexName, + queryId: DETECTION_RESPONSE_ALERTS_BY_STATUS_ID, + skip: !toggleStatus, + }); + const legendItems: LegendItem[] = useMemo( + () => + chartConfigs.map((d) => ({ + color: d.color, + field: legendField, + value: d.label, + })), + [] + ); + + const totalAlerts = + loading || donutData == null + ? 0 + : (donutData?.open?.total ?? 0) + + (donutData?.acknowledged?.total ?? 0) + + (donutData?.closed?.total ?? 0); + + const fillColor: FillColor = useCallback((d: ShapeTreeNode) => { + return chartConfigs.find((cfg) => cfg.label === d.dataName)?.color ?? emptyDonutColor; + }, []); + + return ( + <> + + + {loading && ( + + )} + } + inspectMultiple + toggleStatus={toggleStatus} + toggleQuery={setToggleStatus} + > + + + + {detailsButtonOptions.name} + + + + + {toggleStatus && ( + <> + + + + {loading ? ( + + ) : ( + <> + + + + <> + {ALERTS(totalAlerts)} + + )} + + + + + + } + totalCount={donutData?.open?.total ?? 0} + /> + + + } + totalCount={donutData?.acknowledged?.total ?? 0} + /> + + + } + totalCount={donutData?.closed?.total ?? 0} + /> + + + + + {legendItems.length > 0 && } + + + + + )} + + + + ); +}; diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/alerts_by_status/chart_label.tsx b/x-pack/plugins/security_solution/public/overview/components/detection_response/alerts_by_status/chart_label.tsx new file mode 100644 index 000000000000..ad609102409d --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/alerts_by_status/chart_label.tsx @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; +import styled from 'styled-components'; +import { FormattedCount } from '../../../../common/components/formatted_number'; + +interface ChartLabelProps { + count: number | null | undefined; +} + +const PlaceHolder = styled.div` + padding: ${(props) => props.theme.eui.paddingSizes.s}; +`; + +const ChartLabelComponent: React.FC = ({ count }) => { + return count != null ? ( + + + + ) : ( + + ); +}; + +ChartLabelComponent.displayName = 'ChartLabelComponent'; +export const ChartLabel = React.memo(ChartLabelComponent); diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/index.ts b/x-pack/plugins/security_solution/public/overview/components/detection_response/alerts_by_status/index.ts similarity index 82% rename from x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/index.ts rename to x-pack/plugins/security_solution/public/overview/components/detection_response/alerts_by_status/index.ts index 78c22fc020a4..257e690ba72e 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/index.ts +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/alerts_by_status/index.ts @@ -5,4 +5,4 @@ * 2.0. */ -export * from './trusted_apps_page'; +export { AlertsByStatus } from './alerts_by_status'; diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/alerts_by_status/mock_data.ts b/x-pack/plugins/security_solution/public/overview/components/detection_response/alerts_by_status/mock_data.ts new file mode 100644 index 000000000000..f8aff3bdc87a --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/alerts_by_status/mock_data.ts @@ -0,0 +1,137 @@ +/* + * 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 { AlertsByStatusResponse, AlertsByStatusAgg, ParsedAlertsData } from './types'; + +export const from = '2022-04-05T12:00:00.000Z'; +export const to = '2022-04-08T12:00:00.000Z'; + +export const mockAlertsData: AlertsByStatusResponse<[], AlertsByStatusAgg> = { + took: 4, + _shards: { + total: 1, + successful: 1, + skipped: 0, + failed: 0, + }, + hits: { + total: { + value: 10000, + relation: 'gte', + }, + hits: [], + }, + aggregations: { + alertsByStatus: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'open', + doc_count: 28149, + statusBySeverity: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'low', + doc_count: 22717, + }, + { + key: 'high', + doc_count: 5027, + }, + { + key: 'medium', + doc_count: 405, + }, + ], + }, + }, + { + key: 'closed', + doc_count: 4, + statusBySeverity: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'high', + doc_count: 4, + }, + { + key: 'low', + doc_count: 0, + }, + ], + }, + }, + ], + }, + }, +}; + +export const parsedMockAlertsData: ParsedAlertsData = { + open: { + total: 28149, + severities: [ + { + key: 'low', + label: 'Low', + value: 22717, + }, + { + key: 'high', + label: 'High', + value: 5027, + }, + { + key: 'medium', + label: 'Medium', + value: 405, + }, + ], + }, + closed: { + total: 4, + severities: [ + { + key: 'high', + label: 'High', + value: 4, + }, + { + key: 'low', + label: 'Low', + value: 0, + }, + ], + }, +}; + +export const alertsByStatusQuery = { + size: 0, + query: { + bool: { + filter: [{ range: { '@timestamp': { gte: from, lte: to } } }], + }, + }, + aggs: { + alertsByStatus: { + terms: { + field: 'kibana.alert.workflow_status', + }, + aggs: { + statusBySeverity: { + terms: { + field: 'kibana.alert.severity', + }, + }, + }, + }, + }, +}; diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/alerts_by_status/types.ts b/x-pack/plugins/security_solution/public/overview/components/detection_response/alerts_by_status/types.ts new file mode 100644 index 000000000000..523edf91775f --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/alerts_by_status/types.ts @@ -0,0 +1,61 @@ +/* + * 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 { Severity } from '@kbn/securitysolution-io-ts-alerting-types'; +import { Status } from '../../../../../common/detection_engine/schemas/common/schemas'; + +interface StatusBySeverity { + doc_count_error_upper_bound: number; + sum_other_doc_count: number; + buckets: SeverityBucket[]; +} + +interface StatusBucket { + key: Status; + doc_count: number; + statusBySeverity?: StatusBySeverity; +} + +interface SeverityBucket { + key: Severity; + doc_count: number; +} + +export interface AlertsByStatusAgg { + alertsByStatus: { + doc_count_error_upper_bound: number; + sum_other_doc_count: number; + buckets: StatusBucket[]; + }; +} + +export interface AlertsByStatusResponse { + took: number; + _shards: { + total: number; + successful: number; + skipped: number; + failed: number; + }; + aggregations?: Aggregations; + hits: { + total: { + value: number; + relation: string; + }; + hits: Hit[]; + }; +} + +export interface SeverityBuckets { + key: Severity; + value: number; + label?: string; +} +export type ParsedAlertsData = Partial< + Record +> | null; diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/alerts_by_status/use_alerts_by_status.test.tsx b/x-pack/plugins/security_solution/public/overview/components/detection_response/alerts_by_status/use_alerts_by_status.test.tsx new file mode 100644 index 000000000000..68ee64370b26 --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/alerts_by_status/use_alerts_by_status.test.tsx @@ -0,0 +1,131 @@ +/* + * 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 { renderHook } from '@testing-library/react-hooks'; +import { TestProviders } from '../../../../common/mock'; +import { from, mockAlertsData, alertsByStatusQuery, parsedMockAlertsData, to } from './mock_data'; +import { + useAlertsByStatus, + UseAlertsByStatus, + UseAlertsByStatusProps, +} from './use_alerts_by_status'; + +const dateNow = new Date('2022-04-08T12:00:00.000Z').valueOf(); +const mockDateNow = jest.fn().mockReturnValue(dateNow); +Date.now = jest.fn(() => mockDateNow()) as unknown as DateConstructor['now']; + +const defaultUseQueryAlertsReturn = { + loading: false, + data: null, + setQuery: () => {}, + response: '', + request: '', + refetch: () => {}, +}; +const mockUseQueryAlerts = jest.fn().mockReturnValue(defaultUseQueryAlertsReturn); +jest.mock('../../../../detections/containers/detection_engine/alerts/use_query', () => { + return { + useQueryAlerts: (...props: unknown[]) => mockUseQueryAlerts(...props), + }; +}); + +const mockUseGlobalTime = jest + .fn() + .mockReturnValue({ from, to, setQuery: jest.fn(), deleteQuery: jest.fn() }); +jest.mock('../../../../common/containers/use_global_time', () => { + return { + useGlobalTime: (...props: unknown[]) => mockUseGlobalTime(...props), + }; +}); + +// helper function to render the hook +const renderUseAlertsByStatus = (props: Partial = {}) => + renderHook>( + () => + useAlertsByStatus({ + queryId: 'test', + signalIndexName: 'signal-alerts', + ...props, + }), + { + wrapper: TestProviders, + } + ); + +describe('useAlertsByStatus', () => { + beforeEach(() => { + jest.clearAllMocks(); + mockDateNow.mockReturnValue(dateNow); + mockUseQueryAlerts.mockReturnValue(defaultUseQueryAlertsReturn); + }); + + it('should return default values', () => { + const { result } = renderUseAlertsByStatus(); + + expect(result.current).toEqual({ + items: null, + isLoading: false, + updatedAt: dateNow, + }); + expect(mockUseQueryAlerts).toBeCalledWith({ + query: alertsByStatusQuery, + indexName: 'signal-alerts', + skip: false, + }); + }); + + it('should return parsed items', () => { + mockUseQueryAlerts.mockReturnValue({ + ...defaultUseQueryAlertsReturn, + data: mockAlertsData, + }); + + const { result } = renderUseAlertsByStatus(); + + expect(result.current).toEqual({ + items: parsedMockAlertsData, + isLoading: false, + updatedAt: dateNow, + }); + }); + + it('should return new updatedAt', () => { + const newDateNow = new Date('2022-04-08T14:00:00.000Z').valueOf(); + mockDateNow.mockReturnValue(newDateNow); // setUpdatedAt call + mockDateNow.mockReturnValueOnce(dateNow); // initialization call + + mockUseQueryAlerts.mockReturnValue({ + ...defaultUseQueryAlertsReturn, + data: mockAlertsData, + }); + + const { result } = renderUseAlertsByStatus(); + + expect(mockDateNow).toHaveBeenCalled(); + expect(result.current).toEqual({ + items: parsedMockAlertsData, + isLoading: false, + updatedAt: newDateNow, + }); + }); + + it('should skip the query', () => { + const { result } = renderUseAlertsByStatus({ skip: true }); + + expect(mockUseQueryAlerts).toBeCalledWith({ + query: alertsByStatusQuery, + indexName: 'signal-alerts', + skip: true, + }); + + expect(result.current).toEqual({ + items: null, + isLoading: false, + updatedAt: dateNow, + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/alerts_by_status/use_alerts_by_status.ts b/x-pack/plugins/security_solution/public/overview/components/detection_response/alerts_by_status/use_alerts_by_status.ts new file mode 100644 index 000000000000..979fd6292243 --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/alerts_by_status/use_alerts_by_status.ts @@ -0,0 +1,151 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useCallback, useEffect, useState } from 'react'; +import { Severity } from '@kbn/securitysolution-io-ts-alerting-types'; +import { useGlobalTime } from '../../../../common/containers/use_global_time'; +import { useQueryAlerts } from '../../../../detections/containers/detection_engine/alerts/use_query'; +import { useQueryInspector } from '../../../../common/components/page/manage_query'; +import { AlertsByStatusAgg, AlertsByStatusResponse, ParsedAlertsData } from './types'; +import { + STATUS_CRITICAL_LABEL, + STATUS_HIGH_LABEL, + STATUS_LOW_LABEL, + STATUS_MEDIUM_LABEL, +} from '../translations'; + +export const severityLabels: Record = { + critical: STATUS_CRITICAL_LABEL, + high: STATUS_HIGH_LABEL, + medium: STATUS_MEDIUM_LABEL, + low: STATUS_LOW_LABEL, +}; + +export const getAlertsByStatusQuery = ({ from, to }: { from: string; to: string }) => ({ + size: 0, + query: { + bool: { + filter: [{ range: { '@timestamp': { gte: from, lte: to } } }], + }, + }, + aggs: { + alertsByStatus: { + terms: { + field: 'kibana.alert.workflow_status', + }, + aggs: { + statusBySeverity: { + terms: { + field: 'kibana.alert.severity', + }, + }, + }, + }, + }, +}); + +export const parseAlertsData = ( + response: AlertsByStatusResponse<{}, AlertsByStatusAgg> +): ParsedAlertsData => { + const statusBuckets = response?.aggregations?.alertsByStatus?.buckets ?? []; + + if (statusBuckets.length === 0) { + return null; + } + + return statusBuckets.reduce((parsedAlertsData, statusBucket) => { + const severityBuckets = statusBucket.statusBySeverity?.buckets ?? []; + + return { + ...parsedAlertsData, + [statusBucket.key]: { + total: statusBucket.doc_count, + severities: severityBuckets.map((severityBucket) => ({ + key: severityBucket.key, + value: severityBucket.doc_count, + label: severityLabels[severityBucket.key], + })), + }, + }; + }, {}); +}; + +export interface UseAlertsByStatusProps { + queryId: string; + signalIndexName: string | null; + skip?: boolean; +} + +export type UseAlertsByStatus = (props: UseAlertsByStatusProps) => { + items: ParsedAlertsData; + isLoading: boolean; + updatedAt: number; +}; + +export const useAlertsByStatus: UseAlertsByStatus = ({ + queryId, + signalIndexName, + skip = false, +}) => { + const { to, from, deleteQuery, setQuery } = useGlobalTime(); + const [updatedAt, setUpdatedAt] = useState(Date.now()); + const [items, setItems] = useState(null); + + const { + data, + loading: isLoading, + refetch: refetchQuery, + request, + response, + setQuery: setAlertsQuery, + } = useQueryAlerts<{}, AlertsByStatusAgg>({ + query: getAlertsByStatusQuery({ + from, + to, + }), + indexName: signalIndexName, + skip, + }); + + useEffect(() => { + setAlertsQuery( + getAlertsByStatusQuery({ + from, + to, + }) + ); + }, [setAlertsQuery, from, to]); + + useEffect(() => { + if (data == null) { + setItems(null); + } else { + setItems(parseAlertsData(data)); + } + setUpdatedAt(Date.now()); + }, [data]); + + const refetch = useCallback(() => { + if (!skip && refetchQuery) { + refetchQuery(); + } + }, [skip, refetchQuery]); + + useQueryInspector({ + deleteQuery, + inspect: { + dsl: [request], + response: [response], + }, + refetch, + setQuery, + queryId, + loading: isLoading, + }); + + return { items, isLoading, updatedAt }; +}; diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/rule_alerts_table/rule_alerts_table.tsx b/x-pack/plugins/security_solution/public/overview/components/detection_response/rule_alerts_table/rule_alerts_table.tsx index 7a053fd0366d..a5ddfe25dd98 100644 --- a/x-pack/plugins/security_solution/public/overview/components/detection_response/rule_alerts_table/rule_alerts_table.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/rule_alerts_table/rule_alerts_table.tsx @@ -28,6 +28,8 @@ import { useRuleAlertsItems, RuleAlertsItem } from './use_rule_alerts_items'; import { useNavigation, NavigateTo, GetAppUrl } from '../../../../common/lib/kibana'; import { SecurityPageName } from '../../../../../common/constants'; import { useQueryToggle } from '../../../../common/containers/query_toggle'; +import { HoverVisibilityContainer } from '../../../../common/components/hover_visibility_container'; +import { BUTTON_CLASS as INPECT_BUTTON_CLASS } from '../../../../common/components/inspect'; export interface RuleAlertsTableProps { signalIndexName: string | null; @@ -106,33 +108,35 @@ export const RuleAlertsTable = React.memo(({ signalIndexNa ); return ( - - } - /> - {toggleStatus && ( - <> - {i18n.NO_ALERTS_FOUND}

} titleSize="xs" /> - } - /> - - - {i18n.OPEN_ALL_ALERTS_BUTTON} - - - )} -
+ + + } + /> + {toggleStatus && ( + <> + {i18n.NO_ALERTS_FOUND}} titleSize="xs" /> + } + /> + + + {i18n.OPEN_ALL_ALERTS_BUTTON} + + + )} + + ); }); RuleAlertsTable.displayName = 'RuleAlertsTable'; diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/translations.ts b/x-pack/plugins/security_solution/public/overview/components/detection_response/translations.ts index 81e3bff33545..c2c5b412f1a9 100644 --- a/x-pack/plugins/security_solution/public/overview/components/detection_response/translations.ts +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/translations.ts @@ -7,6 +7,65 @@ import { i18n } from '@kbn/i18n'; +export const STATUS_CRITICAL_LABEL = i18n.translate( + 'xpack.securitySolution.detectionResponse.alertsByStatus.donut.criticalLabel', + { + defaultMessage: 'Critical', + } +); +export const STATUS_HIGH_LABEL = i18n.translate( + 'xpack.securitySolution.detectionResponse.alertsByStatus.donut.highLabel', + { + defaultMessage: 'High', + } +); +export const STATUS_MEDIUM_LABEL = i18n.translate( + 'xpack.securitySolution.detectionResponse.alertsByStatus.donut.mediumLabel', + { + defaultMessage: 'Medium', + } +); +export const STATUS_LOW_LABEL = i18n.translate( + 'xpack.securitySolution.detectionResponse.alertsByStatus.donut.lowLabel', + { + defaultMessage: 'Low', + } +); +export const STATUS_OPEN = i18n.translate( + 'xpack.securitySolution.detectionResponse.alertsByStatus.donut.title.open', + { + defaultMessage: 'Open', + } +); +export const STATUS_ACKNOWLEDGED = i18n.translate( + 'xpack.securitySolution.detectionResponse.alertsByStatus.donut.title.acknowledged', + { + defaultMessage: 'Acknowledged', + } +); +export const STATUS_CLOSED = i18n.translate( + 'xpack.securitySolution.detectionResponse.alertsByStatus.donut.title.closed', + { + defaultMessage: 'Closed', + } +); +export const STATUS_IN_PROGRESS = i18n.translate( + 'xpack.securitySolution.detectionResponse.alertsByStatus.donut.title.inProgress', + { + defaultMessage: 'In progress', + } +); +export const ALERTS = (totalAlerts: number) => + i18n.translate('xpack.securitySolution.detectionResponse.alertsByStatus.totalAlerts', { + values: { totalAlerts }, + defaultMessage: 'total {totalAlerts, plural, =1 {alert} other {alerts}}', + }); +export const ALERTS_TITLE = i18n.translate( + 'xpack.securitySolution.detectionResponse.alertsByStatus.title', + { + defaultMessage: 'Alerts', + } +); export const UPDATING = i18n.translate('xpack.securitySolution.detectionResponse.updating', { defaultMessage: 'Updating...', }); diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/utils.tsx b/x-pack/plugins/security_solution/public/overview/components/detection_response/utils.tsx new file mode 100644 index 000000000000..4ceba6677339 --- /dev/null +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/utils.tsx @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { FormattedRelative } from '@kbn/i18n-react'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import * as i18n from './translations'; + +export const SEVERITY_COLOR = { + critical: '#EF6550', + high: '#EE9266', + medium: '#F3B689', + low: '#F8D9B2', +} as const; + +export interface LastUpdatedAtProps { + updatedAt: number; + isUpdating: boolean; +} +export const LastUpdatedAt: React.FC = ({ isUpdating, updatedAt }) => ( + + {isUpdating ? ( + {i18n.UPDATING} + ) : ( + + <>{i18n.UPDATED} + + + )} + +); diff --git a/x-pack/plugins/security_solution/public/overview/pages/detection_response.tsx b/x-pack/plugins/security_solution/public/overview/pages/detection_response.tsx index 719cb88b6204..2e0030bafa00 100644 --- a/x-pack/plugins/security_solution/public/overview/pages/detection_response.tsx +++ b/x-pack/plugins/security_solution/public/overview/pages/detection_response.tsx @@ -18,6 +18,7 @@ import { RuleAlertsTable } from '../components/detection_response/rule_alerts_ta import { LandingPageComponent } from '../../common/components/landing_page'; import * as i18n from './translations'; import { EmptyPage } from '../../common/components/empty_page'; +import { AlertsByStatus } from '../components/detection_response/alerts_by_status'; const NoPrivilegePage: React.FC = () => { const { docLinks } = useKibana().services; @@ -67,7 +68,11 @@ const DetectionResponseComponent = () => { - {canReadAlerts && {'[alerts chart]'}} + {canReadAlerts && ( + + + + )} {canReadCases && {'[cases chart]'}} diff --git a/x-pack/plugins/security_solution/public/resolver/view/submenu.tsx b/x-pack/plugins/security_solution/public/resolver/view/submenu.tsx index fe25547ef99d..878dbe30a697 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/submenu.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/submenu.tsx @@ -5,66 +5,17 @@ * 2.0. */ -import { i18n } from '@kbn/i18n'; import React, { useMemo, useContext, useCallback } from 'react'; -import { FormattedMessage } from '@kbn/i18n-react'; import { useDispatch } from 'react-redux'; -import { EuiI18nNumber } from '@elastic/eui'; import { EventStats } from '../../../common/endpoint/types'; import { useColors } from './use_colors'; import { useLinkProps } from './use_link_props'; import { ResolverAction } from '../store/actions'; import { SideEffectContext } from './side_effect_context'; +import { FormattedCount } from '../../common/components/formatted_number'; /* eslint-disable react/display-name */ -/** - * Until browser support accomodates the `notation="compact"` feature of Intl.NumberFormat... - * exported for testing - * @param num The number to format - * @returns [mantissa ("12" in "12k+"), Scalar of compact notation (k,M,B,T), remainder indicator ("+" in "12k+")] - */ -export function compactNotationParts( - num: number -): [mantissa: number, compactNotation: string, remainderIndicator: string] { - if (!Number.isFinite(num)) { - return [num, '', '']; - } - - // "scale" here will be a term indicating how many thousands there are in the number - // e.g. 1001 will be 1000, 1000002 will be 1000000, etc. - const scale = Math.pow(10, 3 * Math.min(Math.floor(Math.floor(Math.log10(num)) / 3), 4)); - - const compactPrefixTranslations = { - compactThousands: i18n.translate('xpack.securitySolution.endpoint.resolver.compactThousands', { - defaultMessage: 'k', - }), - compactMillions: i18n.translate('xpack.securitySolution.endpoint.resolver.compactMillions', { - defaultMessage: 'M', - }), - - compactBillions: i18n.translate('xpack.securitySolution.endpoint.resolver.compactBillions', { - defaultMessage: 'B', - }), - - compactTrillions: i18n.translate('xpack.securitySolution.endpoint.resolver.compactTrillions', { - defaultMessage: 'T', - }), - }; - const prefixMap: Map = new Map([ - [1, ''], - [1000, compactPrefixTranslations.compactThousands], - [1000000, compactPrefixTranslations.compactMillions], - [1000000000, compactPrefixTranslations.compactBillions], - [1000000000000, compactPrefixTranslations.compactTrillions], - ]); - const hasRemainder = i18n.translate('xpack.securitySolution.endpoint.resolver.compactOverflow', { - defaultMessage: '+', - }); - const prefix = prefixMap.get(scale) ?? ''; - return [Math.floor(num / scale), prefix, (num / scale) % 1 > Number.EPSILON ? hasRemainder : '']; -} - /** * A Submenu that displays a collection of "pills" for each related event * category it has events for. @@ -89,15 +40,7 @@ export const NodeSubMenuComponents = React.memo( return []; } else { return Object.entries(nodeStats.byCategory).map(([category, total]) => { - const [mantissa, scale, hasRemainder] = compactNotationParts(total || 0); - const prefix = ( - , scale, hasRemainder }} - /> - ); + const prefix = ; return { prefix, category, diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/open_timeline_modal/open_timeline_modal_body.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/open_timeline_modal/open_timeline_modal_body.test.tsx index e2184aaaec77..ddf35eec86e0 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/open_timeline_modal/open_timeline_modal_body.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/open_timeline_modal/open_timeline_modal_body.test.tsx @@ -29,6 +29,7 @@ describe('OpenTimelineModal', () => { euiBreakpoints: { s: '500px', }, + paddingSizes: { m: '16px' }, }, }); const title = 'All Timelines / Open Timelines'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/title_row/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/title_row/index.test.tsx index d46cad3c43a9..9e54d708db49 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/title_row/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/title_row/index.test.tsx @@ -14,7 +14,13 @@ import { TitleRow } from '.'; import { getMockTheme } from '../../../../common/lib/kibana/kibana_react.mock'; const mockTheme = getMockTheme({ - eui: { euiSizeS: '10px', euiLineHeight: 10, euiBreakpoints: { s: '10px' }, euiSize: '10px' }, + eui: { + euiSizeS: '10px', + euiLineHeight: 10, + euiBreakpoints: { s: '10px' }, + euiSize: '10px', + paddingSizes: { m: '16px' }, + }, }); describe('TitleRow', () => { diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/column_header.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/column_header.tsx index 74593e40ddf4..67fcff7de133 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/column_header.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/column_header.tsx @@ -9,7 +9,6 @@ import { EuiContextMenu, EuiContextMenuPanelDescriptor, EuiIcon, EuiPopover } fr import React, { useCallback, useMemo, useRef, useState } from 'react'; import { Draggable } from 'react-beautiful-dnd'; import { Resizable, ResizeCallback } from 're-resizable'; -import deepEqual from 'fast-deep-equal'; import { useDispatch } from 'react-redux'; import styled from 'styled-components'; import { DRAGGABLE_KEYBOARD_WRAPPER_CLASS_NAME } from '@kbn/securitysolution-t-grid'; @@ -298,14 +297,4 @@ const ColumnHeaderComponent: React.FC = ({ ); }; -export const ColumnHeader = React.memo( - ColumnHeaderComponent, - (prevProps, nextProps) => - prevProps.draggableIndex === nextProps.draggableIndex && - prevProps.tabType === nextProps.tabType && - prevProps.timelineId === nextProps.timelineId && - prevProps.isDragging === nextProps.isDragging && - prevProps.onFilterChange === nextProps.onFilterChange && - deepEqual(prevProps.sort, nextProps.sort) && - deepEqual(prevProps.header, nextProps.header) -); +export const ColumnHeader = React.memo(ColumnHeaderComponent); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/index.tsx index 56984452964d..2bd5eda49bd9 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/index.tsx @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import deepEqual from 'fast-deep-equal'; + import React, { useState, useEffect, useCallback, useMemo, useRef } from 'react'; import { Droppable, DraggableChildrenFn } from 'react-beautiful-dnd'; @@ -312,19 +312,4 @@ export const ColumnHeadersComponent = ({ ); }; -export const ColumnHeaders = React.memo( - ColumnHeadersComponent, - (prevProps, nextProps) => - prevProps.actionsColumnWidth === nextProps.actionsColumnWidth && - prevProps.isEventViewer === nextProps.isEventViewer && - prevProps.isSelectAllChecked === nextProps.isSelectAllChecked && - prevProps.onSelectAll === nextProps.onSelectAll && - prevProps.show === nextProps.show && - prevProps.showEventsSelect === nextProps.showEventsSelect && - prevProps.showSelectAllCheckbox === nextProps.showSelectAllCheckbox && - deepEqual(prevProps.sort, nextProps.sort) && - prevProps.timelineId === nextProps.timelineId && - deepEqual(prevProps.columnHeaders, nextProps.columnHeaders) && - prevProps.tabType === nextProps.tabType && - deepEqual(prevProps.browserFields, nextProps.browserFields) -); +export const ColumnHeaders = React.memo(ColumnHeadersComponent); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/providers.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/providers.tsx index 3db938f5efe0..7e52267cd6f1 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/providers.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/providers.tsx @@ -11,7 +11,6 @@ import React, { useCallback, useMemo, useRef, useState } from 'react'; import { Draggable, DraggingStyle, Droppable, NotDraggingStyle } from 'react-beautiful-dnd'; import styled from 'styled-components'; import { useDispatch } from 'react-redux'; -import deepEqual from 'fast-deep-equal'; import { DRAGGABLE_KEYBOARD_WRAPPER_CLASS_NAME, @@ -357,14 +356,7 @@ export const DataProvidersGroupItem = React.memo( ); - }, - (prevProps, nextProps) => - prevProps.groupIndex === nextProps.groupIndex && - prevProps.index === nextProps.index && - prevProps.timelineId === nextProps.timelineId && - deepEqual(prevProps.browserFields, nextProps.browserFields) && - deepEqual(prevProps.group, nextProps.group) && - deepEqual(prevProps.dataProvider, nextProps.dataProvider) + } ); DataProvidersGroupItem.displayName = 'DataProvidersGroupItem'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_bar/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_bar/index.test.tsx index cae0dda0b4bc..b301703f3cae 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_bar/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_bar/index.test.tsx @@ -18,6 +18,8 @@ import { FilterStateStore } from '@kbn/es-query'; import { FilterManager } from '@kbn/data-plugin/public'; import { mockDataProviders } from '../data_providers/mock/mock_data_providers'; import { buildGlobalQuery } from '../helpers'; +import { setAutocomplete } from '@kbn/unified-search-plugin/public/services'; +import { unifiedSearchPluginMock } from '@kbn/unified-search-plugin/public/mocks'; import { QueryBarTimeline, @@ -184,6 +186,11 @@ describe('Timeline QueryBar ', () => { }); describe('#onSavedQuery', () => { + beforeEach(() => { + const autocompleteStart = unifiedSearchPluginMock.createStartContract(); + setAutocomplete(autocompleteStart.autocomplete); + }); + test('is only reference that changed when dataProviders props get updated', async () => { const Proxy = (props: QueryBarTimelineComponentProps) => ( diff --git a/x-pack/plugins/security_solution/public/timelines/containers/index.tsx b/x-pack/plugins/security_solution/public/timelines/containers/index.tsx index 56eb11109d74..fb76d8f2994c 100644 --- a/x-pack/plugins/security_solution/public/timelines/containers/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/containers/index.tsx @@ -15,7 +15,6 @@ import { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/typesWithBo import { DataView, isCompleteResponse, isErrorResponse } from '@kbn/data-plugin/common'; import { ESQuery } from '../../../common/typed_json'; -import { useIsExperimentalFeatureEnabled } from '../../common/hooks/use_experimental_features'; import { inputsModel } from '../../common/store'; import { useKibana } from '../../common/lib/kibana'; import { createFilter } from '../../common/containers/helpers'; @@ -211,9 +210,6 @@ export const useTimelineEvents = ({ }); const { addWarning } = useAppToasts(); - // TODO: Once we are past experimental phase this code should be removed - const ruleRegistryEnabled = useIsExperimentalFeatureEnabled('ruleRegistryEnabled'); - const timelineSearch = useCallback( (request: TimelineRequest | null) => { if (request == null || pageName === '' || skip) { @@ -332,10 +328,7 @@ export const useTimelineEvents = ({ ); useEffect(() => { - if ( - skipQueryForDetectionsPage(id, indexNames, ruleRegistryEnabled) || - indexNames.length === 0 - ) { + if (skipQueryForDetectionsPage(id, indexNames) || indexNames.length === 0) { return; } @@ -397,10 +390,7 @@ export const useTimelineEvents = ({ activeTimeline.setActivePage(newActivePage); } } - if ( - !skipQueryForDetectionsPage(id, indexNames, ruleRegistryEnabled) && - !deepEqual(prevRequest, currentRequest) - ) { + if (!skipQueryForDetectionsPage(id, indexNames) && !deepEqual(prevRequest, currentRequest)) { return currentRequest; } return prevRequest; @@ -416,7 +406,6 @@ export const useTimelineEvents = ({ id, language, limit, - ruleRegistryEnabled, startDate, sort, fields, diff --git a/x-pack/plugins/security_solution/server/config.mock.ts b/x-pack/plugins/security_solution/server/config.mock.ts index bc83a721cbf5..52e68ac548e6 100644 --- a/x-pack/plugins/security_solution/server/config.mock.ts +++ b/x-pack/plugins/security_solution/server/config.mock.ts @@ -44,12 +44,7 @@ const withExperimentalFeature = ( }; }; -const withRuleRegistryEnabled = (config: ConfigType, isEnabled: boolean): ConfigType => { - return isEnabled ? withExperimentalFeature(config, 'ruleRegistryEnabled') : config; -}; - export const configMock = { createDefault: createMockConfig, withExperimentalFeature, - withRuleRegistryEnabled, }; diff --git a/x-pack/plugins/security_solution/server/endpoint/mocks.ts b/x-pack/plugins/security_solution/server/endpoint/mocks.ts index 46e4df691b08..6d6c83efd70f 100644 --- a/x-pack/plugins/security_solution/server/endpoint/mocks.ts +++ b/x-pack/plugins/security_solution/server/endpoint/mocks.ts @@ -5,8 +5,13 @@ * 2.0. */ -import { loggingSystemMock, savedObjectsServiceMock } from '@kbn/core/server/mocks'; -import { IScopedClusterClient, SavedObjectsClientContract } from '@kbn/core/server'; +import type { AwaitedProperties } from '@kbn/utility-types'; +import { + loggingSystemMock, + savedObjectsServiceMock, + ScopedClusterClientMock, +} from '@kbn/core/server/mocks'; +import { SavedObjectsClientContract } from '@kbn/core/server'; import { listMock } from '@kbn/lists-plugin/server/mocks'; import { securityMock } from '@kbn/security-plugin/server/mocks'; import { alertsMock } from '@kbn/alerting-plugin/server/mocks'; @@ -35,7 +40,6 @@ import { import { ManifestManager } from './services/artifacts/manifest_manager/manifest_manager'; import { getManifestManagerMock } from './services/artifacts/manifest_manager/manifest_manager.mock'; import { EndpointAppContext } from './types'; -import { MetadataRequestContext } from './routes/metadata/handlers'; import { SecuritySolutionRequestHandlerContext } from '../types'; import { parseExperimentalConfigValue } from '../../common/experimental_features'; import { requestContextFactoryMock } from '../request_context_factory.mock'; @@ -190,24 +194,22 @@ export const createMockFleetStartContract = (indexPattern: string): FleetStartCo }; }; -export const createMockMetadataRequestContext = (): jest.Mocked => { +export const createMockMetadataRequestContext = () => { return { endpointAppContextService: createMockEndpointAppContextService(), logger: loggingSystemMock.create().get('mock_endpoint_app_context'), - requestHandlerContext: - xpackMocks.createRequestHandlerContext() as unknown as jest.Mocked, + requestHandlerContext: xpackMocks.createRequestHandlerContext() as unknown as jest.Mocked< + AwaitedProperties + >, }; }; export function createRouteHandlerContext( - dataClient: jest.Mocked, + dataClient: ScopedClusterClientMock, savedObjectsClient: jest.Mocked, overrides: { endpointAuthz?: Partial } = {} ) { - const context = requestContextMock.create( - createMockClients(), - overrides - ) as jest.Mocked; + const context = requestContextMock.create(createMockClients(), overrides); context.core.elasticsearch.client = dataClient; context.core.savedObjects.client = savedObjectsClient; return context; diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/actions/audit_log.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/actions/audit_log.test.ts index 960969b41966..c6dbe24cdb90 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/actions/audit_log.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/actions/audit_log.test.ts @@ -9,6 +9,7 @@ import { KibanaResponseFactory, RequestHandler, RouteConfig } from '@kbn/core/server'; import { + coreMock, elasticsearchServiceMock, httpServerMock, httpServiceMock, @@ -162,7 +163,9 @@ describe('Action Log API', () => { path.startsWith(ENDPOINT_ACTION_LOG_ROUTE) )!; await routeHandler( - createRouteHandlerContext(esClientMock, savedObjectsClientMock.create()), + coreMock.createCustomRequestHandlerContext( + createRouteHandlerContext(esClientMock, savedObjectsClientMock.create()) + ) as SecuritySolutionRequestHandlerContext, req, mockResponse ); diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/actions/isolation.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/actions/isolation.test.ts index e17f48017ba6..85d3fd1f064e 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/actions/isolation.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/actions/isolation.test.ts @@ -6,6 +6,8 @@ */ /* eslint-disable @typescript-eslint/no-explicit-any */ +import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { AwaitedProperties } from '@kbn/utility-types'; import { KibanaRequest, KibanaResponseFactory, @@ -20,7 +22,6 @@ import { savedObjectsClientMock, } from '@kbn/core/server/mocks'; import { parseExperimentalConfigValue } from '../../../../common/experimental_features'; -import { SecuritySolutionRequestHandlerContext } from '../../../types'; import { EndpointAppContextService } from '../../endpoint_app_context_services'; import { createMockEndpointAppContextServiceSetupContract, @@ -55,6 +56,7 @@ import { CasesClientMock } from '@kbn/cases-plugin/server/client/mocks'; import { EndpointAuthz } from '../../../../common/endpoint/types/authz'; import type { PackageClient } from '@kbn/fleet-plugin/server'; import { createMockPackageService } from '@kbn/fleet-plugin/server/mocks'; +import { SecuritySolutionRequestHandlerContextMock } from '../../../lib/detection_engine/routes/__mocks__/request_context'; interface CallRouteInterface { body?: HostIsolationRequestBody; @@ -122,7 +124,7 @@ describe('Host Isolation', () => { routePrefix: string, opts: CallRouteInterface, indexExists?: { endpointDsExists: boolean } - ) => Promise>; + ) => Promise>; const superUser = { username: 'superuser', roles: ['superuser'], @@ -190,7 +192,7 @@ describe('Host Isolation', () => { routePrefix: string, { body, idxResponse, searchResponse, mockUser, license, authz = {} }: CallRouteInterface, indexExists?: { endpointDsExists: boolean } - ): Promise> => { + ): Promise> => { const asUser = mockUser ? mockUser : superUser; (startContract.security.authc.getCurrentUser as jest.Mock).mockImplementationOnce( () => asUser @@ -204,31 +206,30 @@ describe('Host Isolation', () => { }; // mock _index_template - ctx.core.elasticsearch.client.asInternalUser.indices.existsIndexTemplate = jest - .fn() - .mockImplementationOnce(() => { + ctx.core.elasticsearch.client.asInternalUser.indices.existsIndexTemplate.mockResponseImplementationOnce( + () => { if (indexExists) { - return Promise.resolve({ + return { body: true, statusCode: 200, - }); + }; } - return Promise.resolve({ + return { body: false, statusCode: 404, - }); - }); + }; + } + ); const withIdxResp = idxResponse ? idxResponse : { statusCode: 201 }; - const mockIndexResponse = jest.fn().mockImplementation(() => Promise.resolve(withIdxResp)); - const mockSearchResponse = jest - .fn() - .mockImplementation(() => - Promise.resolve({ body: legacyMetadataSearchResponseMock(searchResponse) }) - ); - - ctx.core.elasticsearch.client.asInternalUser.index = mockIndexResponse; - ctx.core.elasticsearch.client.asCurrentUser.search = mockSearchResponse; + ctx.core.elasticsearch.client.asInternalUser.index.mockResponseImplementation( + () => withIdxResp + ); + ctx.core.elasticsearch.client.asCurrentUser.search.mockResponseImplementation(() => { + return { + body: legacyMetadataSearchResponseMock(searchResponse), + }; + }); const withLicense = license ? license : Platinum; licenseEmitter.next(withLicense); @@ -241,7 +242,7 @@ describe('Host Isolation', () => { await routeHandler(ctx, mockRequest, mockResponse); - return ctx as unknown as jest.Mocked; + return ctx; }; }); @@ -283,8 +284,9 @@ describe('Host Isolation', () => { searchResponse: metadataResponse, }); const actionDoc: EndpointAction = ( - ctx.core.elasticsearch.client.asInternalUser.index as jest.Mock - ).mock.calls[0][0].body; + ctx.core.elasticsearch.client.asInternalUser.index.mock + .calls[0][0] as estypes.IndexRequest + ).body!; expect(actionDoc.agents).toContain(AgentID); }); it('records the user who performed the action to the action record', async () => { @@ -294,8 +296,9 @@ describe('Host Isolation', () => { mockUser: testU, }); const actionDoc: EndpointAction = ( - ctx.core.elasticsearch.client.asInternalUser.index as jest.Mock - ).mock.calls[0][0].body; + ctx.core.elasticsearch.client.asInternalUser.index.mock + .calls[0][0] as estypes.IndexRequest + ).body!; expect(actionDoc.user_id).toEqual(testU.username); }); it('records the comment in the action payload', async () => { @@ -304,8 +307,9 @@ describe('Host Isolation', () => { body: { endpoint_ids: ['XYZ'], comment: CommentText }, }); const actionDoc: EndpointAction = ( - ctx.core.elasticsearch.client.asInternalUser.index as jest.Mock - ).mock.calls[0][0].body; + ctx.core.elasticsearch.client.asInternalUser.index.mock + .calls[0][0] as estypes.IndexRequest + ).body!; expect(actionDoc.data.comment).toEqual(CommentText); }); it('creates an action and returns its ID', async () => { @@ -313,8 +317,9 @@ describe('Host Isolation', () => { body: { endpoint_ids: ['XYZ'], comment: 'XYZ' }, }); const actionDoc: EndpointAction = ( - ctx.core.elasticsearch.client.asInternalUser.index as jest.Mock - ).mock.calls[0][0].body; + ctx.core.elasticsearch.client.asInternalUser.index.mock + .calls[0][0] as estypes.IndexRequest + ).body!; const actionID = actionDoc.action_id; expect(mockResponse.ok).toBeCalled(); expect((mockResponse.ok.mock.calls[0][0]?.body as HostIsolationResponse).action).toEqual( @@ -326,8 +331,9 @@ describe('Host Isolation', () => { body: { endpoint_ids: ['XYZ'] }, }); const actionDoc: EndpointAction = ( - ctx.core.elasticsearch.client.asInternalUser.index as jest.Mock - ).mock.calls[0][0].body; + ctx.core.elasticsearch.client.asInternalUser.index.mock + .calls[0][0] as estypes.IndexRequest + ).body!; expect(actionDoc.timeout).toEqual(300); }); it('sends the action to the correct agent when endpoint ID is given', async () => { @@ -339,8 +345,9 @@ describe('Host Isolation', () => { searchResponse: doc, }); const actionDoc: EndpointAction = ( - ctx.core.elasticsearch.client.asInternalUser.index as jest.Mock - ).mock.calls[0][0].body; + ctx.core.elasticsearch.client.asInternalUser.index.mock + .calls[0][0] as estypes.IndexRequest + ).body!; expect(actionDoc.agents).toContain(AgentID); }); @@ -349,8 +356,9 @@ describe('Host Isolation', () => { body: { endpoint_ids: ['XYZ'] }, }); const actionDoc: EndpointAction = ( - ctx.core.elasticsearch.client.asInternalUser.index as jest.Mock - ).mock.calls[0][0].body; + ctx.core.elasticsearch.client.asInternalUser.index.mock + .calls[0][0] as estypes.IndexRequest + ).body!; expect(actionDoc.data.command).toEqual('isolate'); }); it('sends the unisolate command payload from the unisolate route', async () => { @@ -358,8 +366,9 @@ describe('Host Isolation', () => { body: { endpoint_ids: ['XYZ'] }, }); const actionDoc: EndpointAction = ( - ctx.core.elasticsearch.client.asInternalUser.index as jest.Mock - ).mock.calls[0][0].body; + ctx.core.elasticsearch.client.asInternalUser.index.mock + .calls[0][0] as estypes.IndexRequest + ).body!; expect(actionDoc.data.command).toEqual('unisolate'); }); @@ -375,14 +384,17 @@ describe('Host Isolation', () => { const indexDoc = ctx.core.elasticsearch.client.asInternalUser.index; const actionDocs: [ - { index: string; body: LogsEndpointAction }, - { index: string; body: EndpointAction } - ] = [(indexDoc as jest.Mock).mock.calls[0][0], (indexDoc as jest.Mock).mock.calls[1][0]]; + { index: string; body?: LogsEndpointAction }, + { index: string; body?: EndpointAction } + ] = [ + indexDoc.mock.calls[0][0] as estypes.IndexRequest, + indexDoc.mock.calls[1][0] as estypes.IndexRequest, + ]; expect(actionDocs[0].index).toEqual(ENDPOINT_ACTIONS_INDEX); expect(actionDocs[1].index).toEqual(AGENT_ACTIONS_INDEX); - expect(actionDocs[0].body.EndpointActions.data.command).toEqual('unisolate'); - expect(actionDocs[1].body.data.command).toEqual('unisolate'); + expect(actionDocs[0].body!.EndpointActions.data.command).toEqual('unisolate'); + expect(actionDocs[1].body!.data.command).toEqual('unisolate'); }); it('handles isolation', async () => { @@ -395,14 +407,17 @@ describe('Host Isolation', () => { ); const indexDoc = ctx.core.elasticsearch.client.asInternalUser.index; const actionDocs: [ - { index: string; body: LogsEndpointAction }, - { index: string; body: EndpointAction } - ] = [(indexDoc as jest.Mock).mock.calls[0][0], (indexDoc as jest.Mock).mock.calls[1][0]]; + { index: string; body?: LogsEndpointAction }, + { index: string; body?: EndpointAction } + ] = [ + indexDoc.mock.calls[0][0] as estypes.IndexRequest, + indexDoc.mock.calls[1][0] as estypes.IndexRequest, + ]; expect(actionDocs[0].index).toEqual(ENDPOINT_ACTIONS_INDEX); expect(actionDocs[1].index).toEqual(AGENT_ACTIONS_INDEX); - expect(actionDocs[0].body.EndpointActions.data.command).toEqual('isolate'); - expect(actionDocs[1].body.data.command).toEqual('isolate'); + expect(actionDocs[0].body!.EndpointActions.data.command).toEqual('isolate'); + expect(actionDocs[1].body!.data.command).toEqual('isolate'); }); it('handles errors', async () => { diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/actions/isolation.ts b/x-pack/plugins/security_solution/server/endpoint/routes/actions/isolation.ts index 101a55d7acba..69954dbd7e7a 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/actions/isolation.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/actions/isolation.ts @@ -84,7 +84,7 @@ const createFailedActionResponseEntry = async ({ logger: Logger; }): Promise => { // 8.0+ requires internal user to write to system indices - const esClient = context.core.elasticsearch.client.asInternalUser; + const esClient = (await context.core).elasticsearch.client.asInternalUser; try { await esClient.index({ index: `${ENDPOINT_ACTION_RESPONSES_DS}-default`, @@ -178,7 +178,7 @@ export const isolationRequestHandler = function ( }); // 8.0+ requires internal user to write to system indices - const esClient = context.core.elasticsearch.client.asInternalUser; + const esClient = (await context.core).elasticsearch.client.asInternalUser; // if the new endpoint indices/data streams exists // write the action request to the new endpoint index diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/actions/status.ts b/x-pack/plugins/security_solution/server/endpoint/routes/actions/status.ts index 9b134e385be6..bfd17cccb3d0 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/actions/status.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/actions/status.ts @@ -47,7 +47,7 @@ export const actionStatusRequestHandler = function ( SecuritySolutionRequestHandlerContext > { return async (context, req, res) => { - const esClient = context.core.elasticsearch.client.asInternalUser; + const esClient = (await context.core).elasticsearch.client.asInternalUser; const agentIDs: string[] = Array.isArray(req.query.agent_ids) ? [...new Set(req.query.agent_ids)] : [req.query.agent_ids]; diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/enrichment.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/enrichment.test.ts index b508469b88f5..0aaa138c90d3 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/enrichment.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/enrichment.test.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { coreMock } from '@kbn/core/server/mocks'; import { HostStatus } from '../../../../common/endpoint/types'; import { createMockMetadataRequestContext } from '../../mocks'; import { EndpointDocGenerator } from '../../../../common/endpoint/generate_data'; @@ -12,13 +13,21 @@ import { enrichHostMetadata, MetadataRequestContext } from './handlers'; import { AgentClient } from '@kbn/fleet-plugin/server'; describe('test document enrichment', () => { - let metaReqCtx: jest.Mocked; + let metaReqCtx: ReturnType; const docGen = new EndpointDocGenerator(); beforeEach(() => { metaReqCtx = createMockMetadataRequestContext(); }); + const getMetadataRequestContext = () => + ({ + ...metaReqCtx, + requestHandlerContext: coreMock.createCustomRequestHandlerContext( + metaReqCtx.requestHandlerContext + ), + } as unknown as MetadataRequestContext); + describe('host status enrichment', () => { let statusFn: jest.Mock; @@ -32,49 +41,70 @@ describe('test document enrichment', () => { it('should return host healthy for online agent', async () => { statusFn.mockImplementation(() => 'online'); - const enrichedHostList = await enrichHostMetadata(docGen.generateHostMetadata(), metaReqCtx); + const enrichedHostList = await enrichHostMetadata( + docGen.generateHostMetadata(), + getMetadataRequestContext() + ); expect(enrichedHostList.host_status).toEqual(HostStatus.HEALTHY); }); it('should return host offline for offline agent', async () => { statusFn.mockImplementation(() => 'offline'); - const enrichedHostList = await enrichHostMetadata(docGen.generateHostMetadata(), metaReqCtx); + const enrichedHostList = await enrichHostMetadata( + docGen.generateHostMetadata(), + getMetadataRequestContext() + ); expect(enrichedHostList.host_status).toEqual(HostStatus.OFFLINE); }); it('should return host updating for unenrolling agent', async () => { statusFn.mockImplementation(() => 'unenrolling'); - const enrichedHostList = await enrichHostMetadata(docGen.generateHostMetadata(), metaReqCtx); + const enrichedHostList = await enrichHostMetadata( + docGen.generateHostMetadata(), + getMetadataRequestContext() + ); expect(enrichedHostList.host_status).toEqual(HostStatus.UPDATING); }); it('should return host unhealthy for degraded agent', async () => { statusFn.mockImplementation(() => 'degraded'); - const enrichedHostList = await enrichHostMetadata(docGen.generateHostMetadata(), metaReqCtx); + const enrichedHostList = await enrichHostMetadata( + docGen.generateHostMetadata(), + getMetadataRequestContext() + ); expect(enrichedHostList.host_status).toEqual(HostStatus.UNHEALTHY); }); it('should return host unhealthy for erroring agent', async () => { statusFn.mockImplementation(() => 'error'); - const enrichedHostList = await enrichHostMetadata(docGen.generateHostMetadata(), metaReqCtx); + const enrichedHostList = await enrichHostMetadata( + docGen.generateHostMetadata(), + getMetadataRequestContext() + ); expect(enrichedHostList.host_status).toEqual(HostStatus.UNHEALTHY); }); it('should return host unhealthy for warning agent', async () => { statusFn.mockImplementation(() => 'warning'); - const enrichedHostList = await enrichHostMetadata(docGen.generateHostMetadata(), metaReqCtx); + const enrichedHostList = await enrichHostMetadata( + docGen.generateHostMetadata(), + getMetadataRequestContext() + ); expect(enrichedHostList.host_status).toEqual(HostStatus.UNHEALTHY); }); it('should return host unhealthy for invalid agent', async () => { statusFn.mockImplementation(() => 'asliduasofb'); - const enrichedHostList = await enrichHostMetadata(docGen.generateHostMetadata(), metaReqCtx); + const enrichedHostList = await enrichHostMetadata( + docGen.generateHostMetadata(), + getMetadataRequestContext() + ); expect(enrichedHostList.host_status).toEqual(HostStatus.UNHEALTHY); }); }); @@ -109,7 +139,10 @@ describe('test document enrichment', () => { }; }); - const enrichedHostList = await enrichHostMetadata(docGen.generateHostMetadata(), metaReqCtx); + const enrichedHostList = await enrichHostMetadata( + docGen.generateHostMetadata(), + getMetadataRequestContext() + ); expect(enrichedHostList.policy_info).toBeDefined(); expect(enrichedHostList.policy_info?.agent.applied.id).toEqual(policyID); expect(enrichedHostList.policy_info?.agent.applied.revision).toEqual(policyRev); @@ -125,7 +158,10 @@ describe('test document enrichment', () => { }; }); - const enrichedHostList = await enrichHostMetadata(docGen.generateHostMetadata(), metaReqCtx); + const enrichedHostList = await enrichHostMetadata( + docGen.generateHostMetadata(), + getMetadataRequestContext() + ); expect(enrichedHostList.policy_info).toBeDefined(); expect(enrichedHostList.policy_info?.agent.configured.id).toEqual(policyID); expect(enrichedHostList.policy_info?.agent.configured.revision).toEqual(policyRev); @@ -146,7 +182,10 @@ describe('test document enrichment', () => { }; }); - const enrichedHostList = await enrichHostMetadata(docGen.generateHostMetadata(), metaReqCtx); + const enrichedHostList = await enrichHostMetadata( + docGen.generateHostMetadata(), + getMetadataRequestContext() + ); expect(enrichedHostList.policy_info).toBeDefined(); expect(enrichedHostList.policy_info?.endpoint.id).toEqual(policyID); expect(enrichedHostList.policy_info?.endpoint.revision).toEqual(policyRev); diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/handlers.ts b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/handlers.ts index a8d186e126b6..1b86924101b6 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/handlers.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/handlers.ts @@ -94,7 +94,7 @@ export function getMetadataListRequestHandler( return async (context, request, response) => { const endpointMetadataService = endpointAppContext.service.getEndpointMetadataService(); const fleetServices = endpointAppContext.service.getScopedFleetServices(request); - const esClient = context.core.elasticsearch.client.asInternalUser; + const esClient = (await context.core).elasticsearch.client.asInternalUser; let doesUnitedIndexExist = false; let didUnitedIndexError = false; @@ -168,9 +168,10 @@ export const getMetadataRequestHandler = function ( const endpointMetadataService = endpointAppContext.service.getEndpointMetadataService(); try { + const esClient = (await context.core).elasticsearch.client; return response.ok({ body: await endpointMetadataService.getEnrichedHostMetadata( - context.core.elasticsearch.client.asInternalUser, + esClient.asInternalUser, endpointAppContext.service.getScopedFleetServices(request), request.params.id ), @@ -185,7 +186,7 @@ export function getMetadataTransformStatsHandler( logger: Logger ): RequestHandler { return async (context, _, response) => { - const esClient = context.core.elasticsearch.client.asInternalUser; + const esClient = (await context.core).elasticsearch.client.asInternalUser; try { const transformStats = await esClient.transform.getTransformStats({ transform_id: METADATA_TRANSFORMS_PATTERN, @@ -235,19 +236,14 @@ export async function enrichHostMetadata( let hostStatus = HostStatus.UNHEALTHY; let elasticAgentId = hostMetadata?.elastic?.agent?.id; const log = metadataRequestContext.logger; + const coreContext = await metadataRequestContext.requestHandlerContext?.core; try { - if ( - !metadataRequestContext.esClient && - !metadataRequestContext.requestHandlerContext?.core.elasticsearch.client - ) { + if (!metadataRequestContext.esClient && !coreContext?.elasticsearch.client) { throw new Error('esClient not found'); } - if ( - !metadataRequestContext.savedObjectsClient && - !metadataRequestContext.requestHandlerContext?.core.savedObjects - ) { + if (!metadataRequestContext.savedObjectsClient && !coreContext?.savedObjects) { throw new Error('esSavedObjectClient not found'); } } catch (e) { @@ -257,8 +253,8 @@ export async function enrichHostMetadata( const esSavedObjectClient = metadataRequestContext?.savedObjectsClient ?? - (metadataRequestContext.requestHandlerContext?.core.savedObjects - .client as SavedObjectsClientContract); + (coreContext?.savedObjects.client as SavedObjectsClientContract); + const fleetContext = await metadataRequestContext.requestHandlerContext?.fleet; try { /** @@ -270,10 +266,7 @@ export async function enrichHostMetadata( log.warn(`Missing elastic agent id, using host id instead ${elasticAgentId}`); } - const status = - await metadataRequestContext.requestHandlerContext?.fleet?.agentClient.asCurrentUser.getAgentStatusById( - elasticAgentId - ); + const status = await fleetContext?.agentClient.asCurrentUser.getAgentStatusById(elasticAgentId); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion hostStatus = fleetAgentStatusToEndpointHostStatus(status!); } catch (e) { @@ -287,10 +280,7 @@ export async function enrichHostMetadata( let policyInfo: HostInfo['policy_info']; try { - const agent = - await metadataRequestContext.requestHandlerContext?.fleet?.agentClient.asCurrentUser.getAgent( - elasticAgentId - ); + const agent = await fleetContext?.agentClient.asCurrentUser.getAgent(elasticAgentId); const agentPolicy = await metadataRequestContext.endpointAppContextService .getAgentPolicyService() // eslint-disable-next-line @typescript-eslint/no-non-null-assertion @@ -338,17 +328,18 @@ async function legacyListMetadataQuery( queryOptions: GetMetadataListRequestQuery ): Promise { const fleetAgentClient = fleetServices.agent; + const coreContext = await context.core; const metadataRequestContext: MetadataRequestContext = { - esClient: context.core.elasticsearch.client, + esClient: coreContext.elasticsearch.client, endpointAppContextService: endpointAppContext.service, logger, requestHandlerContext: context, - savedObjectsClient: context.core.savedObjects.client, + savedObjectsClient: coreContext.savedObjects.client, }; const endpointPolicyIds = endpointPolicies.map((policy) => policy.policy_id); - const esClient = context.core.elasticsearch.client.asInternalUser; + const esClient = coreContext.elasticsearch.client.asInternalUser; const unenrolledAgentIds = await findAllUnenrolledAgentIds(fleetAgentClient, endpointPolicyIds); diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/policy/handlers.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/policy/handlers.test.ts index 95c3e24709cc..01136fef8188 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/policy/handlers.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/policy/handlers.test.ts @@ -24,7 +24,10 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { GetHostPolicyResponse, HostPolicyResponse } from '../../../../common/endpoint/types'; import { EndpointDocGenerator } from '../../../../common/endpoint/generate_data'; import { parseExperimentalConfigValue } from '../../../../common/experimental_features'; -import { createMockConfig } from '../../../lib/detection_engine/routes/__mocks__'; +import { + createMockConfig, + requestContextMock, +} from '../../../lib/detection_engine/routes/__mocks__'; import { Agent } from '@kbn/fleet-plugin/common/types/models'; import { AgentClient, AgentService } from '@kbn/fleet-plugin/server/services'; import { get } from 'lodash'; @@ -59,7 +62,9 @@ describe('test policy response handler', () => { }); await hostPolicyResponseHandler( - createRouteHandlerContext(mockScopedClient, mockSavedObjectClient), + requestContextMock.convertContext( + createRouteHandlerContext(mockScopedClient, mockSavedObjectClient) + ), mockRequest, mockResponse ); @@ -81,7 +86,9 @@ describe('test policy response handler', () => { }); await hostPolicyResponseHandler( - createRouteHandlerContext(mockScopedClient, mockSavedObjectClient), + requestContextMock.convertContext( + createRouteHandlerContext(mockScopedClient, mockSavedObjectClient) + ), mockRequest, mockResponse ); @@ -185,7 +192,9 @@ describe('test policy response handler', () => { }); await policySummarysHandler( - createRouteHandlerContext(mockScopedClient, mockSavedObjectClient), + requestContextMock.convertContext( + createRouteHandlerContext(mockScopedClient, mockSavedObjectClient) + ), mockRequest, mockResponse ); @@ -216,7 +225,9 @@ describe('test policy response handler', () => { }); await agentPolicySummaryHandler( - createRouteHandlerContext(mockScopedClient, mockSavedObjectClient), + requestContextMock.convertContext( + createRouteHandlerContext(mockScopedClient, mockSavedObjectClient) + ), mockRequest, mockResponse ); diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/policy/handlers.ts b/x-pack/plugins/security_solution/server/endpoint/routes/policy/handlers.ts index 8a32580f9669..7ccae199a3ed 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/policy/handlers.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/policy/handlers.ts @@ -22,11 +22,8 @@ export const getHostPolicyResponseHandler = function (): RequestHandler< undefined > { return async (context, request, response) => { - const doc = await getPolicyResponseByAgentId( - policyIndexPattern, - request.query.agentId, - context.core.elasticsearch.client - ); + const client = (await context.core).elasticsearch.client; + const doc = await getPolicyResponseByAgentId(policyIndexPattern, request.query.agentId, client); if (doc) { return response.ok({ body: doc }); diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/entity.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/entity.ts index 58ccd119c4dd..ef438667c364 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/entity.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/entity.ts @@ -87,7 +87,8 @@ export function handleEntities(): RequestHandler> { return async (context, req, res) => { - const client = context.core.elasticsearch.client; + const client = (await context.core).elasticsearch.client; const fetcher = new Fetcher(client); const body = await fetcher.tree(req.body); return res.ok({ diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/with_endpoint_authz.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/with_endpoint_authz.test.ts index 607b9a8c2350..34f2f7a434fb 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/with_endpoint_authz.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/with_endpoint_authz.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { httpServerMock, loggingSystemMock } from '@kbn/core/server/mocks'; +import { coreMock, httpServerMock, loggingSystemMock } from '@kbn/core/server/mocks'; import { RequestHandler } from '@kbn/core/server'; import { requestContextMock } from '../../lib/detection_engine/routes/__mocks__'; import { EndpointApiNeededAuthz, withEndpointAuthz } from './with_endpoint_authz'; @@ -14,7 +14,7 @@ import { EndpointAuthorizationError } from '../errors'; describe('When using `withEndpointAuthz()`', () => { let mockRequestHandler: jest.Mocked; - let mockContext: jest.Mocked>; + let mockContext: ReturnType; let mockRequest: ReturnType; let mockResponse: ReturnType; let logger: ReturnType; @@ -58,10 +58,10 @@ describe('When using `withEndpointAuthz()`', () => { }, { canCreateArtifactsByPolicy: false }, ], - ])('should grant access when needed authz is %j', (neededAuthz, authzOverrides) => { + ])('should grant access when needed authz is %j', async (neededAuthz, authzOverrides) => { Object.assign(mockContext.securitySolution.endpointAuthz, authzOverrides); - withEndpointAuthz(neededAuthz, logger, mockRequestHandler)( - mockContext, + await withEndpointAuthz(neededAuthz, logger, mockRequestHandler)( + coreMock.createCustomRequestHandlerContext(mockContext), mockRequest, mockResponse ); @@ -85,11 +85,11 @@ describe('When using `withEndpointAuthz()`', () => { }, { canCreateArtifactsByPolicy: false }, ], - ])('should deny access when not authorized for %j', (neededAuthz, authzOverrides) => { + ])('should deny access when not authorized for %j', async (neededAuthz, authzOverrides) => { Object.assign(mockContext.securitySolution.endpointAuthz, authzOverrides); - withEndpointAuthz(neededAuthz, logger, mockRequestHandler)( - mockContext, + await withEndpointAuthz(neededAuthz, logger, mockRequestHandler)( + coreMock.createCustomRequestHandlerContext(mockContext), mockRequest, mockResponse ); diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/with_endpoint_authz.ts b/x-pack/plugins/security_solution/server/endpoint/routes/with_endpoint_authz.ts index 0ed4adb01a9b..14a61a92e52e 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/with_endpoint_authz.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/with_endpoint_authz.ts @@ -51,7 +51,7 @@ export const withEndpointAuthz = ( SecuritySolutionRequestHandlerContext > = async (context, request, response) => { if (enforceAuthz) { - const endpointAuthz = context.securitySolution.endpointAuthz; + const endpointAuthz = (await context.securitySolution).endpointAuthz; const permissionChecker = (permission: EndpointAuthzKeyList[0]) => endpointAuthz[permission]; // has `all`? diff --git a/x-pack/plugins/security_solution/server/endpoint/services/metadata/metadata.ts b/x-pack/plugins/security_solution/server/endpoint/services/metadata/metadata.ts index bd2cdd7e45a3..9e21c2c192b8 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/metadata/metadata.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/metadata/metadata.ts @@ -18,7 +18,7 @@ export async function getMetadataForEndpoints( requestHandlerContext: SecuritySolutionRequestHandlerContext ): Promise { const query = getESQueryHostMetadataByIDs(endpointIDs); - const esClient = requestHandlerContext.core.elasticsearch.client.asCurrentUser; + const esClient = (await requestHandlerContext.core).elasticsearch.client.asCurrentUser; const { body } = await esClient.search(query as estypes.SearchRequest, { meta: true, }); diff --git a/x-pack/plugins/security_solution/server/endpoint/utils/audit_log_helpers.ts b/x-pack/plugins/security_solution/server/endpoint/utils/audit_log_helpers.ts index 8d9234fab280..3a1e25c32b68 100644 --- a/x-pack/plugins/security_solution/server/endpoint/utils/audit_log_helpers.ts +++ b/x-pack/plugins/security_solution/server/endpoint/utils/audit_log_helpers.ts @@ -194,7 +194,7 @@ export const getActionRequestsResult = async ({ let actionRequests: TransportResult, unknown>; try { - const esClient = context.core.elasticsearch.client.asInternalUser; + const esClient = (await context.core).elasticsearch.client.asInternalUser; actionRequests = await esClient.search(actionsSearchQuery, { ...queryOptions, meta: true }); const actionIds = actionRequests?.body?.hits?.hits?.map((e) => { return logsEndpointActionsRegex.test(e._index) @@ -251,7 +251,7 @@ export const getActionResponsesResult = async ({ let actionResponses: TransportResult, unknown>; try { - const esClient = context.core.elasticsearch.client.asInternalUser; + const esClient = (await context.core).elasticsearch.client.asInternalUser; actionResponses = await esClient.search(responsesSearchQuery, { ...queryOptions, meta: true }); } catch (error) { logger.error(error); diff --git a/x-pack/plugins/security_solution/server/endpoint/utils/yes_no_data_stream.test.ts b/x-pack/plugins/security_solution/server/endpoint/utils/yes_no_data_stream.test.ts index 43042a332c1d..eb53a5854c2a 100644 --- a/x-pack/plugins/security_solution/server/endpoint/utils/yes_no_data_stream.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/utils/yes_no_data_stream.test.ts @@ -6,6 +6,7 @@ */ import { + coreMock, elasticsearchServiceMock, savedObjectsClientMock, loggingSystemMock, @@ -18,7 +19,7 @@ import { } from './yes_no_data_stream'; describe('Accurately answers if index template for data stream exists', () => { - let ctxt: jest.Mocked; + let ctxt: ReturnType; beforeEach(() => { ctxt = createRouteHandlerContext( @@ -27,17 +28,17 @@ describe('Accurately answers if index template for data stream exists', () => { ); }); - const mockEsApiResponse = (response: { body: boolean; statusCode: number }) => { - return jest.fn().mockImplementationOnce(() => Promise.resolve(response)); - }; - it('Returns FALSE for a non-existent data stream index template', async () => { - ctxt.core.elasticsearch.client.asInternalUser.indices.existsIndexTemplate = mockEsApiResponse({ - body: false, - statusCode: 404, - }); + ctxt.core.elasticsearch.client.asInternalUser.indices.existsIndexTemplate.mockResponseImplementation( + () => ({ + body: false, + statusCode: 404, + }) + ); const doesItExist = await doLogsEndpointActionDsExists({ - context: ctxt, + context: coreMock.createCustomRequestHandlerContext( + ctxt + ) as SecuritySolutionRequestHandlerContext, logger: loggingSystemMock.create().get('host-isolation'), dataStreamName: '.test-stream.name', }); @@ -45,12 +46,16 @@ describe('Accurately answers if index template for data stream exists', () => { }); it('Returns TRUE for an existing index', async () => { - ctxt.core.elasticsearch.client.asInternalUser.indices.existsIndexTemplate = mockEsApiResponse({ - body: true, - statusCode: 200, - }); + ctxt.core.elasticsearch.client.asInternalUser.indices.existsIndexTemplate.mockResponseImplementation( + () => ({ + body: true, + statusCode: 200, + }) + ); const doesItExist = await doLogsEndpointActionDsExists({ - context: ctxt, + context: coreMock.createCustomRequestHandlerContext( + ctxt + ) as SecuritySolutionRequestHandlerContext, logger: loggingSystemMock.create().get('host-isolation'), dataStreamName: '.test-stream.name', }); @@ -59,7 +64,7 @@ describe('Accurately answers if index template for data stream exists', () => { }); describe('Accurately answers if index exists', () => { - let ctxt: jest.Mocked; + let ctxt: ReturnType; beforeEach(() => { ctxt = createRouteHandlerContext( @@ -68,17 +73,15 @@ describe('Accurately answers if index exists', () => { ); }); - const mockEsApiResponse = (response: { body: boolean; statusCode: number }) => { - return jest.fn().mockImplementationOnce(() => Promise.resolve(response)); - }; - it('Returns FALSE for a non-existent index', async () => { - ctxt.core.elasticsearch.client.asInternalUser.indices.exists = mockEsApiResponse({ + ctxt.core.elasticsearch.client.asInternalUser.indices.exists.mockResponseImplementation(() => ({ body: false, statusCode: 404, - }); + })); const doesItExist = await doesLogsEndpointActionsIndexExist({ - context: ctxt, + context: coreMock.createCustomRequestHandlerContext( + ctxt + ) as SecuritySolutionRequestHandlerContext, logger: loggingSystemMock.create().get('host-isolation'), indexName: '.test-index.name-default', }); @@ -86,12 +89,14 @@ describe('Accurately answers if index exists', () => { }); it('Returns TRUE for an existing index', async () => { - ctxt.core.elasticsearch.client.asInternalUser.indices.exists = mockEsApiResponse({ + ctxt.core.elasticsearch.client.asInternalUser.indices.exists.mockResponseImplementation(() => ({ body: true, statusCode: 200, - }); + })); const doesItExist = await doesLogsEndpointActionsIndexExist({ - context: ctxt, + context: coreMock.createCustomRequestHandlerContext( + ctxt + ) as SecuritySolutionRequestHandlerContext, logger: loggingSystemMock.create().get('host-isolation'), indexName: '.test-index.name-default', }); diff --git a/x-pack/plugins/security_solution/server/endpoint/utils/yes_no_data_stream.ts b/x-pack/plugins/security_solution/server/endpoint/utils/yes_no_data_stream.ts index b84a026f9e8e..9e94e4ba29c3 100644 --- a/x-pack/plugins/security_solution/server/endpoint/utils/yes_no_data_stream.ts +++ b/x-pack/plugins/security_solution/server/endpoint/utils/yes_no_data_stream.ts @@ -18,7 +18,7 @@ export const doLogsEndpointActionDsExists = async ({ dataStreamName: string; }): Promise => { try { - const esClient = context.core.elasticsearch.client.asInternalUser; + const esClient = (await context.core).elasticsearch.client.asInternalUser; const doesIndexTemplateExist = await esClient.indices.existsIndexTemplate( { name: dataStreamName, @@ -46,7 +46,7 @@ export const doesLogsEndpointActionsIndexExist = async ({ indexName: string; }): Promise => { try { - const esClient = context.core.elasticsearch.client.asInternalUser; + const esClient = (await context.core).elasticsearch.client.asInternalUser; const doesIndexExist = await esClient.indices.exists( { index: indexName, diff --git a/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.test.ts b/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.test.ts index 80bafe6b4799..17bfa0d584f2 100644 --- a/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.test.ts +++ b/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.test.ts @@ -33,7 +33,6 @@ import { Subject } from 'rxjs'; import { ILicense } from '@kbn/licensing-plugin/common/types'; import { EndpointDocGenerator } from '../../common/endpoint/generate_data'; import { ProtectionModes } from '../../common/endpoint/types'; -import type { SecuritySolutionRequestHandlerContext } from '../types'; import { getExceptionListClientMock } from '@kbn/lists-plugin/server/services/exception_lists/exception_list_client.mock'; import { getExceptionListSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_schema.mock'; import { ExceptionListClient } from '@kbn/lists-plugin/server'; @@ -50,7 +49,7 @@ import { ALL_ENDPOINT_ARTIFACT_LIST_IDS } from '../../common/endpoint/service/ar describe('ingest_integration tests ', () => { let endpointAppContextMock: EndpointAppContextServiceStartContract; let req: KibanaRequest; - let ctx: SecuritySolutionRequestHandlerContext; + let ctx: ReturnType; const exceptionListClient: ExceptionListClient = getExceptionListClientMock(); let licenseEmitter: Subject; let licenseService: LicenseService; @@ -99,7 +98,7 @@ describe('ingest_integration tests ', () => { exceptionListClient ); - return callback(createNewPackagePolicyMock(), ctx, req); + return callback(createNewPackagePolicyMock(), requestContextMock.convertContext(ctx), req); }; const TEST_POLICY_ID_1 = 'c6d16e42-c32d-4dce-8a88-113cfe276ad1'; @@ -265,9 +264,9 @@ describe('ingest_integration tests ', () => { ); const policyConfig = generator.generatePolicyPackagePolicy(); policyConfig.inputs[0]!.config!.policy.value = mockPolicy; - await expect(() => callback(policyConfig, ctx, req)).rejects.toThrow( - 'Requires Platinum license' - ); + await expect(() => + callback(policyConfig, requestContextMock.convertContext(ctx), req) + ).rejects.toThrow('Requires Platinum license'); }); it('updates successfully if no paid features are turned on in the policy', async () => { const mockPolicy = policyFactoryWithoutPaidFeatures(); @@ -281,7 +280,11 @@ describe('ingest_integration tests ', () => { ); const policyConfig = generator.generatePolicyPackagePolicy(); policyConfig.inputs[0]!.config!.policy.value = mockPolicy; - const updatedPolicyConfig = await callback(policyConfig, ctx, req); + const updatedPolicyConfig = await callback( + policyConfig, + requestContextMock.convertContext(ctx), + req + ); expect(updatedPolicyConfig.inputs[0]!.config!.policy.value).toEqual(mockPolicy); }); }); @@ -302,7 +305,11 @@ describe('ingest_integration tests ', () => { ); const policyConfig = generator.generatePolicyPackagePolicy(); policyConfig.inputs[0]!.config!.policy.value = mockPolicy; - const updatedPolicyConfig = await callback(policyConfig, ctx, req); + const updatedPolicyConfig = await callback( + policyConfig, + requestContextMock.convertContext(ctx), + req + ); expect(updatedPolicyConfig.inputs[0]!.config!.policy.value).toEqual(mockPolicy); }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/legacy_rules_notification_alert_type.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/legacy_rules_notification_alert_type.test.ts index 0facf2968127..16ce9c7d2a3e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/legacy_rules_notification_alert_type.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/legacy_rules_notification_alert_type.test.ts @@ -6,11 +6,12 @@ */ import { loggingSystemMock } from '@kbn/core/server/mocks'; -import { getAlertMock } from '../routes/__mocks__/request_responses'; +import { alertsMock, RuleExecutorServicesMock } from '@kbn/alerting-plugin/server/mocks'; + +import { getRuleMock } from '../routes/__mocks__/request_responses'; // eslint-disable-next-line no-restricted-imports import { legacyRulesNotificationAlertType } from './legacy_rules_notification_alert_type'; import { buildSignalsSearchQuery } from './build_signals_query'; -import { alertsMock, RuleExecutorServicesMock } from '@kbn/alerting-plugin/server/mocks'; // eslint-disable-next-line no-restricted-imports import { LegacyNotificationExecutorOptions } from './legacy_types'; import { @@ -75,10 +76,7 @@ describe('legacyRules_notification_alert_type', () => { }); }); - describe.each([ - ['Legacy', false], - ['RAC', true], - ])('executor - %s', (_, isRuleRegistryEnabled) => { + describe('executor', () => { it('throws an error if rule alert was not found', async () => { alertServices.savedObjectsClient.get.mockResolvedValue({ id: 'id', @@ -93,7 +91,7 @@ describe('legacyRules_notification_alert_type', () => { }); it('should call buildSignalsSearchQuery with proper params', async () => { - const ruleAlert = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const ruleAlert = getRuleMock(getQueryRuleParams()); alertServices.savedObjectsClient.get.mockResolvedValue({ id: 'id', type: 'type', @@ -118,7 +116,7 @@ describe('legacyRules_notification_alert_type', () => { }); it('should resolve results_link when meta is undefined to use "/app/security"', async () => { - const ruleAlert = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const ruleAlert = getRuleMock(getQueryRuleParams()); delete ruleAlert.params.meta; alertServices.savedObjectsClient.get.mockResolvedValue({ id: 'rule-id', @@ -144,7 +142,7 @@ describe('legacyRules_notification_alert_type', () => { }); it('should resolve results_link when meta is an empty object to use "/app/security"', async () => { - const ruleAlert = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const ruleAlert = getRuleMock(getQueryRuleParams()); ruleAlert.params.meta = {}; alertServices.savedObjectsClient.get.mockResolvedValue({ id: 'rule-id', @@ -169,7 +167,7 @@ describe('legacyRules_notification_alert_type', () => { }); it('should resolve results_link to custom kibana link when given one', async () => { - const ruleAlert = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const ruleAlert = getRuleMock(getQueryRuleParams()); ruleAlert.params.meta = { kibana_siem_app_url: 'http://localhost', }; @@ -196,7 +194,7 @@ describe('legacyRules_notification_alert_type', () => { }); it('should not call alertFactory.create if signalsCount was 0', async () => { - const ruleAlert = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const ruleAlert = getRuleMock(getQueryRuleParams()); alertServices.savedObjectsClient.get.mockResolvedValue({ id: 'id', type: 'type', @@ -213,7 +211,7 @@ describe('legacyRules_notification_alert_type', () => { }); it('should call scheduleActions if signalsCount was greater than 0', async () => { - const ruleAlert = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const ruleAlert = getRuleMock(getQueryRuleParams()); alertServices.savedObjectsClient.get.mockResolvedValue({ id: 'id', type: 'type', diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_context.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_context.ts index ad0c3eace42c..60e8f0c8cf85 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_context.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_context.ts @@ -6,6 +6,7 @@ */ import type { MockedKeys } from '@kbn/utility-types/jest'; +import type { AwaitedProperties } from '@kbn/utility-types'; import { coreMock } from '@kbn/core/server/mocks'; import { ActionsApiRequestHandlerContext } from '@kbn/actions-plugin/server'; @@ -62,10 +63,11 @@ export const createMockClients = () => { type MockClients = ReturnType; -type SecuritySolutionRequestHandlerContextMock = - MockedKeys & { - core: MockClients['core']; - }; +export type SecuritySolutionRequestHandlerContextMock = MockedKeys< + AwaitedProperties> +> & { + core: MockClients['core']; +}; const createRequestContextMock = ( clients: MockClients = createMockClients(), @@ -89,6 +91,14 @@ const createRequestContextMock = ( }; }; +const convertRequestContextMock = ( + context: AwaitedProperties +): SecuritySolutionRequestHandlerContext => { + return coreMock.createCustomRequestHandlerContext( + context + ) as unknown as SecuritySolutionRequestHandlerContext; +}; + const createSecuritySolutionRequestContextMock = ( clients: MockClients, overrides: { endpointAuthz?: Partial } = {} @@ -127,6 +137,7 @@ const createTools = () => { export const requestContextMock = { create: createRequestContextMock, + convertContext: convertRequestContextMock, createMockClients, createTools, }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_responses.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_responses.ts index b79ae4b38abb..1304a06fa7dc 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_responses.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_responses.ts @@ -206,18 +206,18 @@ export const getEmptyFindResult = (): FindHit => ({ data: [], }); -export const getFindResultWithSingleHit = (isRuleRegistryEnabled: boolean): FindHit => ({ +export const getFindResultWithSingleHit = (): FindHit => ({ page: 1, perPage: 1, total: 1, - data: [getAlertMock(isRuleRegistryEnabled, getQueryRuleParams())], + data: [getRuleMock(getQueryRuleParams())], }); -export const nonRuleFindResult = (isRuleRegistryEnabled: boolean): FindHit => ({ +export const nonRuleFindResult = (): FindHit => ({ page: 1, perPage: 1, total: 1, - data: [nonRuleAlert(isRuleRegistryEnabled)], + data: [nonRuleAlert()], }); export const getFindResultWithMultiHits = ({ @@ -381,22 +381,19 @@ export const createActionResult = (): ActionResult => ({ isPreconfigured: false, }); -export const nonRuleAlert = (isRuleRegistryEnabled: boolean) => ({ +export const nonRuleAlert = () => ({ // Defaulting to QueryRuleParams because ts doesn't like empty objects - ...getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()), + ...getRuleMock(getQueryRuleParams()), id: '04128c15-0d1b-4716-a4c5-46997ac7f3bc', name: 'Non-Rule Alert', alertTypeId: 'something', }); -export const getAlertMock = ( - isRuleRegistryEnabled: boolean, - params: T -): SanitizedRule => ({ +export const getRuleMock = (params: T): SanitizedRule => ({ id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', name: 'Detect Root/Admin Users', tags: [`${INTERNAL_RULE_ID_KEY}:rule-1`, `${INTERNAL_IMMUTABLE_KEY}:false`], - alertTypeId: isRuleRegistryEnabled ? ruleTypeMappings[params.type] : 'siem.signals', + alertTypeId: ruleTypeMappings[params.type], consumer: 'siem', params, createdAt: new Date('2019-12-13T16:40:33.400Z'), @@ -418,12 +415,9 @@ export const getAlertMock = ( }, }); -export const resolveAlertMock = ( - isRuleRegistryEnabled: boolean, - params: T -): ResolvedSanitizedRule => ({ +export const resolveRuleMock = (params: T): ResolvedSanitizedRule => ({ outcome: 'exactMatch', - ...getAlertMock(isRuleRegistryEnabled, params), + ...getRuleMock(params), }); export const updateActionResult = (): ActionResult => ({ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/server.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/server.ts index 85cf6a07f4d8..ef2a3016e5f9 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/server.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/server.ts @@ -17,6 +17,7 @@ interface Route { config: RouteConfig; handler: RequestHandler; } + const getRoute = (routerMock: MockServer['router']): Route => { const routeCalls = [ ...routerMock.get.mock.calls, @@ -41,7 +42,7 @@ class MockServer { constructor( public readonly router = httpServiceMock.createRouter(), private responseMock = responseFactoryMock.create(), - private contextMock = requestContextMock.create(), + private contextMock = requestContextMock.convertContext(requestContextMock.create()), private resultMock = buildResultMock() ) {} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/create_index_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/create_index_route.ts index 86e06a71b638..9879d4f83cfb 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/create_index_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/create_index_route.ts @@ -47,11 +47,12 @@ export const createIndexRoute = (router: SecuritySolutionPluginRouter) => { const siemResponse = buildSiemResponse(response); try { - const siemClient = context.securitySolution?.getAppClient(); + const securitySolution = await context.securitySolution; + const siemClient = securitySolution?.getAppClient(); if (!siemClient) { return siemResponse.error({ statusCode: 404 }); } - await createDetectionIndex(context.securitySolution); + await createDetectionIndex(securitySolution); return response.ok({ body: { acknowledged: true } }); } catch (err) { const error = transformError(err); @@ -67,7 +68,6 @@ export const createIndexRoute = (router: SecuritySolutionPluginRouter) => { export const createDetectionIndex = async ( context: SecuritySolutionApiRequestHandlerContext ): Promise => { - const config = context.getConfig(); const esClient = context.core.elasticsearch.client.asCurrentUser; const siemClient = context.getAppClient(); const spaceId = context.getSpaceId(); @@ -77,11 +77,10 @@ export const createDetectionIndex = async ( context.core.elasticsearch.client.asInternalUser, index ); - const { ruleRegistryEnabled } = config.experimentalFeatures; - // If using the rule registry implementation, we don't want to create new .siem-signals indices - - // only create/update resources if there are existing indices - if (ruleRegistryEnabled && !indexExists) { + // We don't want to create new .siem-signals indices - only create/update + // resources if there are existing indices + if (!indexExists) { return; } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/delete_index_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/delete_index_route.ts index 6eae3908e215..ddaa49782360 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/delete_index_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/delete_index_route.ts @@ -40,9 +40,9 @@ export const deleteIndexRoute = (router: SecuritySolutionPluginRouter) => { const siemResponse = buildSiemResponse(response); try { - const esClient = context.core.elasticsearch.client.asCurrentUser; + const esClient = (await context.core).elasticsearch.client.asCurrentUser; - const siemClient = context.securitySolution?.getAppClient(); + const siemClient = (await context.securitySolution)?.getAppClient(); if (!siemClient) { return siemResponse.error({ statusCode: 404 }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/read_index_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/read_index_route.ts index 24f05c707e3d..c03f8e000d0f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/read_index_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/read_index_route.ts @@ -32,19 +32,22 @@ export const readIndexRoute = ( const siemResponse = buildSiemResponse(response); try { - const siemClient = context.securitySolution?.getAppClient(); - const esClient = context.core.elasticsearch.client.asCurrentUser; + const core = await context.core; + const securitySolution = await context.securitySolution; + + const siemClient = securitySolution?.getAppClient(); + const esClient = core.elasticsearch.client.asCurrentUser; if (!siemClient) { return siemResponse.error({ statusCode: 404 }); } - const spaceId = context.securitySolution.getSpaceId(); + const spaceId = securitySolution.getSpaceId(); const indexName = ruleDataService.getResourceName(`security.alerts-${spaceId}`); const index = siemClient.getSignalsIndex(); const indexExists = await getBootstrapIndexExists( - context.core.elasticsearch.client.asInternalUser, + core.elasticsearch.client.asInternalUser, index ); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/privileges/read_privileges_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/privileges/read_privileges_route.test.ts index c4c205e69606..002b4c2264ee 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/privileges/read_privileges_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/privileges/read_privileges_route.test.ts @@ -30,7 +30,7 @@ describe('read_privileges route', () => { test('returns 200 when doing a normal request', async () => { const response = await server.inject( getPrivilegeRequest({ auth: { isAuthenticated: false } }), - context + requestContextMock.convertContext(context) ); expect(response.status).toEqual(200); }); @@ -38,7 +38,7 @@ describe('read_privileges route', () => { test('returns the payload when doing a normal request', async () => { const response = await server.inject( getPrivilegeRequest({ auth: { isAuthenticated: false } }), - context + requestContextMock.convertContext(context) ); const expectedBody = { ...getMockPrivilegesResult(), @@ -58,7 +58,7 @@ describe('read_privileges route', () => { const response = await server.inject( getPrivilegeRequest({ auth: { isAuthenticated: true } }), - context + requestContextMock.convertContext(context) ); expect(response.status).toEqual(200); expect(response.body).toEqual(expectedBody); @@ -70,7 +70,7 @@ describe('read_privileges route', () => { ); const response = await server.inject( getPrivilegeRequest({ auth: { isAuthenticated: false } }), - context + requestContextMock.convertContext(context) ); expect(response.status).toEqual(500); expect(response.body).toEqual({ message: 'Test error', status_code: 500 }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/privileges/read_privileges_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/privileges/read_privileges_route.ts index 22f8ccb39118..31ea057facb6 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/privileges/read_privileges_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/privileges/read_privileges_route.ts @@ -28,15 +28,17 @@ export const readPrivilegesRoute = ( const siemResponse = buildSiemResponse(response); try { - const esClient = context.core.elasticsearch.client.asCurrentUser; - const siemClient = context.securitySolution?.getAppClient(); + const core = await context.core; + const securitySolution = await context.securitySolution; + const esClient = core.elasticsearch.client.asCurrentUser; + const siemClient = securitySolution?.getAppClient(); if (!siemClient) { return siemResponse.error({ statusCode: 404 }); } - const spaceId = context.securitySolution.getSpaceId(); - const index = context.securitySolution + const spaceId = securitySolution.getSpaceId(); + const index = securitySolution .getRuleDataService() .getResourceName(`security.alerts-${spaceId}`); const clusterPrivileges = await readPrivileges(esClient, index); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.test.ts index d5b301364d4a..327b8afb46b5 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.test.ts @@ -9,10 +9,10 @@ import { getEmptyFindResult, addPrepackagedRulesRequest, getFindResultWithSingleHit, - getAlertMock, + getRuleMock, getBasicEmptySearchResponse, } from '../__mocks__/request_responses'; -import { configMock, requestContextMock, serverMock } from '../__mocks__'; +import { requestContextMock, serverMock } from '../__mocks__'; import { AddPrepackagedRulesSchemaDecoded } from '../../../../../common/detection_engine/schemas/request/add_prepackaged_rules_schema'; import { addPrepackedRulesRoute, createPrepackagedRules } from './add_prepackaged_rules_route'; import { listMock } from '@kbn/lists-plugin/server/mocks'; @@ -74,19 +74,14 @@ describe('add_prepackaged_rules_route', () => { let server: ReturnType; let { clients, context } = requestContextMock.createTools(); let mockExceptionsClient: ExceptionListClient; - const defaultConfig = context.securitySolution.getConfig(); beforeEach(() => { server = serverMock.create(); ({ clients, context } = requestContextMock.createTools()); mockExceptionsClient = listMock.getExceptionListClient(); - context.securitySolution.getConfig.mockImplementation(() => - configMock.withRuleRegistryEnabled(defaultConfig, true) - ); - - clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(true)); - clients.rulesClient.update.mockResolvedValue(getAlertMock(true, getQueryRuleParams())); + clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); + clients.rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); (installPrepackagedTimelines as jest.Mock).mockReset(); (installPrepackagedTimelines as jest.Mock).mockResolvedValue({ @@ -106,7 +101,7 @@ describe('add_prepackaged_rules_route', () => { describe('status codes', () => { test('returns 200', async () => { const request = addPrepackagedRulesRequest(); - const response = await server.inject(request, context); + const response = await server.inject(request, requestContextMock.convertContext(context)); expect(response.status).toEqual(200); }); @@ -116,7 +111,7 @@ describe('add_prepackaged_rules_route', () => { test('1 rule is installed and 0 are updated when find results are empty', async () => { clients.rulesClient.find.mockResolvedValue(getEmptyFindResult()); const request = addPrepackagedRulesRequest(); - const response = await server.inject(request, context); + const response = await server.inject(request, requestContextMock.convertContext(context)); expect(response.status).toEqual(200); expect(response.body).toEqual({ @@ -129,7 +124,7 @@ describe('add_prepackaged_rules_route', () => { test('1 rule is updated and 0 are installed when we return a single find and the versions are different', async () => { const request = addPrepackagedRulesRequest(); - const response = await server.inject(request, context); + const response = await server.inject(request, requestContextMock.convertContext(context)); expect(response.status).toEqual(200); expect(response.body).toEqual({ @@ -159,7 +154,7 @@ describe('add_prepackaged_rules_route', () => { ], }); const request = addPrepackagedRulesRequest(); - const response = await server.inject(request, context); + const response = await server.inject(request, requestContextMock.convertContext(context)); expect(response.body).toEqual({ rules_installed: 0, rules_updated: 1, @@ -178,7 +173,7 @@ describe('add_prepackaged_rules_route', () => { errors: [], }); const request = addPrepackagedRulesRequest(); - const response = await server.inject(request, context); + const response = await server.inject(request, requestContextMock.convertContext(context)); expect(response.body).toEqual({ rules_installed: 0, rules_updated: 1, @@ -197,7 +192,7 @@ describe('add_prepackaged_rules_route', () => { errors: [], }); const request = addPrepackagedRulesRequest(); - const response = await server.inject(request, context); + const response = await server.inject(request, requestContextMock.convertContext(context)); expect(response.body).toEqual({ rules_installed: 0, rules_updated: 1, @@ -224,7 +219,7 @@ describe('add_prepackaged_rules_route', () => { ], }); const request = addPrepackagedRulesRequest(); - const response = await server.inject(request, context); + const response = await server.inject(request, requestContextMock.convertContext(context)); expect(response.body).toEqual({ rules_installed: 0, rules_updated: 1, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.ts index 0042030ea21e..145558cd4a10 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.ts @@ -6,7 +6,7 @@ */ import moment from 'moment'; -import { transformError, getIndexExists } from '@kbn/securitysolution-es-utils'; +import { transformError } from '@kbn/securitysolution-es-utils'; import { validate } from '@kbn/securitysolution-io-ts-utils'; import { RulesClient } from '@kbn/alerting-plugin/server'; import { ExceptionListClient } from '@kbn/lists-plugin/server'; @@ -54,10 +54,10 @@ export const addPrepackedRulesRoute = (router: SecuritySolutionPluginRouter) => const siemResponse = buildSiemResponse(response); try { - const rulesClient = context.alerting.getRulesClient(); + const rulesClient = (await context.alerting).getRulesClient(); const validated = await createPrepackagedRules( - context.securitySolution, + await context.securitySolution, rulesClient, undefined ); @@ -88,7 +88,6 @@ export const createPrepackagedRules = async ( ): Promise => { const config = context.getConfig(); const frameworkRequest = context.getFrameworkRequest(); - const esClient = context.core.elasticsearch.client; const savedObjectsClient = context.core.savedObjects.client; const siemClient = context.getAppClient(); const exceptionsListClient = context.getExceptionListClient() ?? exceptionsClient; @@ -98,7 +97,6 @@ export const createPrepackagedRules = async ( maxTimelineImportExportSize, prebuiltRulesFromFileSystem, prebuiltRulesFromSavedObjects, - experimentalFeatures: { ruleRegistryEnabled }, } = config; if (!siemClient || !rulesClient) { @@ -117,24 +115,12 @@ export const createPrepackagedRules = async ( ); const prepackagedRules = await getExistingPrepackagedRules({ rulesClient, - isRuleRegistryEnabled: ruleRegistryEnabled, }); const rulesToInstall = getRulesToInstall(latestPrepackagedRules, prepackagedRules); const rulesToUpdate = getRulesToUpdate(latestPrepackagedRules, prepackagedRules); const signalsIndex = siemClient.getSignalsIndex(); - if (!ruleRegistryEnabled && (rulesToInstall.length !== 0 || rulesToUpdate.length !== 0)) { - const signalsIndexExists = await getIndexExists(esClient.asCurrentUser, signalsIndex); - if (!signalsIndexExists) { - throw new PrepackagedRulesError( - `Pre-packaged rules cannot be installed until the signals index is created: ${signalsIndex}`, - 400 - ); - } - } - await Promise.all( - installPrepackagedRules(rulesClient, rulesToInstall, signalsIndex, ruleRegistryEnabled) - ); + await Promise.all(installPrepackagedRules(rulesClient, rulesToInstall, signalsIndex)); const timeline = await installPrepackagedTimelines( maxTimelineImportExportSize, frameworkRequest, @@ -149,7 +135,6 @@ export const createPrepackagedRules = async ( savedObjectsClient, rulesToUpdate, signalsIndex, - ruleRegistryEnabled, context.getRuleExecutionLog() ); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_bulk_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_bulk_route.test.ts index 82813b18cbd0..8b23aca24c79 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_bulk_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_bulk_route.test.ts @@ -12,7 +12,7 @@ import { getReadBulkRequest, getFindResultWithSingleHit, getEmptyFindResult, - getAlertMock, + getRuleMock, createBulkMlRuleRequest, getBasicEmptySearchResponse, getBasicNoShardsSearchResponse, @@ -27,10 +27,7 @@ import { loggingSystemMock } from '@kbn/core/server/mocks'; jest.mock('../../../machine_learning/authz', () => mockMlAuthzFactory.create()); -describe.each([ - ['Legacy', false], - ['RAC', true], -])('create_rules_bulk - %s', (_, isRuleRegistryEnabled) => { +describe('create_rules_bulk', () => { let server: ReturnType; let { clients, context } = requestContextMock.createTools(); let ml: ReturnType; @@ -42,19 +39,20 @@ describe.each([ const logger = loggingSystemMock.createLogger(); clients.rulesClient.find.mockResolvedValue(getEmptyFindResult()); // no existing rules - clients.rulesClient.create.mockResolvedValue( - getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()) - ); // successful creation + clients.rulesClient.create.mockResolvedValue(getRuleMock(getQueryRuleParams())); // successful creation context.core.elasticsearch.client.asCurrentUser.search.mockResolvedValue( elasticsearchClientMock.createSuccessTransportRequestPromise(getBasicEmptySearchResponse()) ); - createRulesBulkRoute(server.router, ml, isRuleRegistryEnabled, logger); + createRulesBulkRoute(server.router, ml, logger); }); describe('status codes', () => { test('returns 200', async () => { - const response = await server.inject(getReadBulkRequest(), context); + const response = await server.inject( + getReadBulkRequest(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(200); }); }); @@ -67,7 +65,10 @@ describe.each([ .mockResolvedValue({ valid: false, message: 'mocked validation message' }), }); - const response = await server.inject(createBulkMlRuleRequest(), context); + const response = await server.inject( + createBulkMlRuleRequest(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(200); expect(response.body).toEqual([ { @@ -86,27 +87,20 @@ describe.each([ getBasicNoShardsSearchResponse() ) ); - const response = await server.inject(getReadBulkRequest(), context); + const response = await server.inject( + getReadBulkRequest(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(200); - - if (!isRuleRegistryEnabled) { - expect(response.body).toEqual([ - { - error: { - message: - 'To create a rule, the index must exist first. Index undefined does not exist', - status_code: 400, - }, - rule_id: 'rule-1', - }, - ]); - } }); test('returns a duplicate error if rule_id already exists', async () => { - clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled)); - const response = await server.inject(getReadBulkRequest(), context); + clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); + const response = await server.inject( + getReadBulkRequest(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(200); expect(response.body).toEqual([ @@ -123,7 +117,10 @@ describe.each([ clients.rulesClient.create.mockImplementation(async () => { throw new Error('Test error'); }); - const response = await server.inject(getReadBulkRequest(), context); + const response = await server.inject( + getReadBulkRequest(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(200); expect(response.body).toEqual([ @@ -142,7 +139,7 @@ describe.each([ path: DETECTION_ENGINE_RULES_BULK_CREATE, body: [getCreateRulesSchemaMock(), getCreateRulesSchemaMock()], }); - const response = await server.inject(request, context); + const response = await server.inject(request, requestContextMock.convertContext(context)); expect(response.status).toEqual(200); expect(response.body).toEqual([ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts index cf4633b97681..d0028bbf7752 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts @@ -6,7 +6,6 @@ */ import { validate } from '@kbn/securitysolution-io-ts-utils'; -import { getIndexExists } from '@kbn/securitysolution-es-utils'; import { Logger } from '@kbn/core/server'; import { createRuleValidateTypeDependents } from '../../../../../common/detection_engine/schemas/request/create_rules_type_dependents'; import { createRulesBulkSchema } from '../../../../../common/detection_engine/schemas/request/create_rules_bulk_schema'; @@ -34,7 +33,6 @@ import { getDeprecatedBulkEndpointHeader, logDeprecatedBulkEndpoint } from './ut export const createRulesBulkRoute = ( router: SecuritySolutionPluginRouter, ml: SetupPlugins['ml'], - isRuleRegistryEnabled: boolean, logger: Logger ) => { router.post( @@ -51,13 +49,15 @@ export const createRulesBulkRoute = ( logDeprecatedBulkEndpoint(logger, DETECTION_ENGINE_RULES_BULK_CREATE); const siemResponse = buildSiemResponse(response); - const rulesClient = context.alerting.getRulesClient(); - const esClient = context.core.elasticsearch.client; - const savedObjectsClient = context.core.savedObjects.client; - const siemClient = context.securitySolution.getAppClient(); + + const ctx = await context.resolve(['core', 'securitySolution', 'licensing', 'alerting']); + + const rulesClient = ctx.alerting.getRulesClient(); + const savedObjectsClient = ctx.core.savedObjects.client; + const siemClient = ctx.securitySolution.getAppClient(); const mlAuthz = buildMlAuthz({ - license: context.licensing.license, + license: ctx.licensing.license, ml, request, savedObjectsClient, @@ -73,7 +73,6 @@ export const createRulesBulkRoute = ( if (payloadRule.rule_id != null) { const rule = await readRules({ id: undefined, - isRuleRegistryEnabled, rulesClient, ruleId: payloadRule.rule_id, }); @@ -85,11 +84,7 @@ export const createRulesBulkRoute = ( }); } } - const internalRule = convertCreateAPIToInternalSchema( - payloadRule, - siemClient, - isRuleRegistryEnabled - ); + const internalRule = convertCreateAPIToInternalSchema(payloadRule, siemClient); try { const validationErrors = createRuleValidateTypeDependents(payloadRule); if (validationErrors.length) { @@ -101,15 +96,6 @@ export const createRulesBulkRoute = ( } throwAuthzError(await mlAuthz.validateRuleType(internalRule.params.type)); - const finalIndex = internalRule.params.outputIndex; - const indexExists = await getIndexExists(esClient.asCurrentUser, finalIndex); - if (!isRuleRegistryEnabled && !indexExists) { - return createBulkErrorObject({ - ruleId: internalRule.params.ruleId, - statusCode: 400, - message: `To create a rule, the index must exist first. Index ${finalIndex} does not exist`, - }); - } const createdRule = await rulesClient.create({ data: internalRule, @@ -120,12 +106,7 @@ export const createRulesBulkRoute = ( await rulesClient.muteAll({ id: createdRule.id }); } - return transformValidateBulkError( - internalRule.params.ruleId, - createdRule, - null, - isRuleRegistryEnabled - ); + return transformValidateBulkError(internalRule.params.ruleId, createdRule, null); } catch (err) { return transformBulkError( internalRule.params.ruleId, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_route.test.ts index fb2625f149d8..163a6b8e4322 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_route.test.ts @@ -8,13 +8,12 @@ import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; import { getEmptyFindResult, - getAlertMock, + getRuleMock, getCreateRequest, getRuleExecutionSummarySucceeded, getFindResultWithSingleHit, createMlRuleRequest, getBasicEmptySearchResponse, - getBasicNoShardsSearchResponse, } from '../__mocks__/request_responses'; import { mlServicesMock, mlAuthzMock as mockMlAuthzFactory } from '../../../machine_learning/mocks'; import { buildMlAuthz } from '../../../machine_learning/authz'; @@ -24,12 +23,10 @@ import { getCreateRulesSchemaMock } from '../../../../../common/detection_engine // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { elasticsearchClientMock } from '@kbn/core/server/elasticsearch/client/mocks'; import { getQueryRuleParams } from '../../schemas/rule_schemas.mock'; + jest.mock('../../../machine_learning/authz', () => mockMlAuthzFactory.create()); -describe.each([ - ['Legacy', false], - ['RAC', true], -])('create_rules - %s', (_, isRuleRegistryEnabled) => { +describe('create_rules', () => { let server: ReturnType; let { clients, context } = requestContextMock.createTools(); let ml: ReturnType; @@ -40,9 +37,7 @@ describe.each([ ml = mlServicesMock.createSetupContract(); clients.rulesClient.find.mockResolvedValue(getEmptyFindResult()); // no current rules - clients.rulesClient.create.mockResolvedValue( - getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()) - ); // creation succeeds + clients.rulesClient.create.mockResolvedValue(getRuleMock(getQueryRuleParams())); // creation succeeds clients.ruleExecutionLog.getExecutionSummary.mockResolvedValue( getRuleExecutionSummarySucceeded() ); @@ -50,26 +45,35 @@ describe.each([ context.core.elasticsearch.client.asCurrentUser.search.mockResolvedValue( elasticsearchClientMock.createSuccessTransportRequestPromise(getBasicEmptySearchResponse()) ); - createRulesRoute(server.router, ml, isRuleRegistryEnabled); + createRulesRoute(server.router, ml); }); describe('status codes', () => { test('returns 200 with a rule created via RulesClient', async () => { - const response = await server.inject(getCreateRequest(), context); + const response = await server.inject( + getCreateRequest(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(200); }); test('returns 200 if license is not platinum', async () => { (context.licensing.license.hasAtLeast as jest.Mock).mockReturnValue(false); - const response = await server.inject(getCreateRequest(), context); + const response = await server.inject( + getCreateRequest(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(200); }); }); describe('creating an ML Rule', () => { test('is successful', async () => { - const response = await server.inject(createMlRuleRequest(), context); + const response = await server.inject( + createMlRuleRequest(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(200); }); @@ -80,7 +84,10 @@ describe.each([ .mockResolvedValue({ valid: false, message: 'mocked validation message' }), }); - const response = await server.inject(createMlRuleRequest(), context); + const response = await server.inject( + createMlRuleRequest(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(403); expect(response.body).toEqual({ message: 'mocked validation message', @@ -90,27 +97,12 @@ describe.each([ }); describe('unhappy paths', () => { - test('it returns a 400 if the index does not exist when rule registry not enabled', async () => { - context.core.elasticsearch.client.asCurrentUser.search.mockResolvedValueOnce( - elasticsearchClientMock.createSuccessTransportRequestPromise( - getBasicNoShardsSearchResponse() - ) - ); - const response = await server.inject(getCreateRequest(), context); - - expect(response.status).toEqual(isRuleRegistryEnabled ? 200 : 400); - - if (!isRuleRegistryEnabled) { - expect(response.body).toEqual({ - message: 'To create a rule, the index must exist first. Index undefined does not exist', - status_code: 400, - }); - } - }); - test('returns a duplicate error if rule_id already exists', async () => { - clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled)); - const response = await server.inject(getCreateRequest(), context); + clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); + const response = await server.inject( + getCreateRequest(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(409); expect(response.body).toEqual({ @@ -123,7 +115,10 @@ describe.each([ clients.rulesClient.create.mockImplementation(async () => { throw new Error('Test error'); }); - const response = await server.inject(getCreateRequest(), context); + const response = await server.inject( + getCreateRequest(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(500); expect(response.body).toEqual({ message: 'Test error', diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_route.ts index fe39c5cb9680..b44b2fd0bd6a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_route.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { transformError, getIndexExists } from '@kbn/securitysolution-es-utils'; +import { transformError } from '@kbn/securitysolution-es-utils'; import { buildRouteValidation } from '../../../../utils/build_validation/route_validation'; import { DETECTION_ENGINE_RULES_URL, @@ -25,8 +25,7 @@ import { convertCreateAPIToInternalSchema } from '../../schemas/rule_converters' export const createRulesRoute = ( router: SecuritySolutionPluginRouter, - ml: SetupPlugins['ml'], - isRuleRegistryEnabled: boolean + ml: SetupPlugins['ml'] ): void => { router.post( { @@ -46,15 +45,21 @@ export const createRulesRoute = ( } try { - const rulesClient = context.alerting.getRulesClient(); - const ruleExecutionLog = context.securitySolution.getRuleExecutionLog(); - const esClient = context.core.elasticsearch.client; - const savedObjectsClient = context.core.savedObjects.client; - const siemClient = context.securitySolution.getAppClient(); + const ctx = await context.resolve([ + 'core', + 'securitySolution', + 'licensing', + 'alerting', + 'lists', + ]); + + const rulesClient = ctx.alerting.getRulesClient(); + const ruleExecutionLog = ctx.securitySolution.getRuleExecutionLog(); + const savedObjectsClient = ctx.core.savedObjects.client; + const siemClient = ctx.securitySolution.getAppClient(); if (request.body.rule_id != null) { const rule = await readRules({ - isRuleRegistryEnabled, rulesClient, ruleId: request.body.rule_id, id: undefined, @@ -67,33 +72,18 @@ export const createRulesRoute = ( } } - const internalRule = convertCreateAPIToInternalSchema( - request.body, - siemClient, - isRuleRegistryEnabled - ); + const internalRule = convertCreateAPIToInternalSchema(request.body, siemClient); const mlAuthz = buildMlAuthz({ - license: context.licensing.license, + license: ctx.licensing.license, ml, request, savedObjectsClient, }); throwAuthzError(await mlAuthz.validateRuleType(internalRule.params.type)); - const indexExists = await getIndexExists( - esClient.asCurrentUser, - internalRule.params.outputIndex - ); - if (!isRuleRegistryEnabled && !indexExists) { - return siemResponse.error({ - statusCode: 400, - body: `To create a rule, the index must exist first. Index ${internalRule.params.outputIndex} does not exist`, - }); - } - // This will create the endpoint list if it does not exist yet - await context.lists?.getExceptionListClient().createEndpointList(); + await ctx.lists?.getExceptionListClient().createEndpointList(); const createdRule = await rulesClient.create({ data: internalRule, @@ -106,11 +96,7 @@ export const createRulesRoute = ( const ruleExecutionSummary = await ruleExecutionLog.getExecutionSummary(createdRule.id); - const [validated, errors] = newTransformValidate( - createdRule, - ruleExecutionSummary, - isRuleRegistryEnabled - ); + const [validated, errors] = newTransformValidate(createdRule, ruleExecutionSummary); if (errors != null) { return siemResponse.error({ statusCode: 500, body: errors }); } else { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.test.ts index c8d1628ded78..01683599e4cf 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.test.ts @@ -19,10 +19,7 @@ import { requestContextMock, serverMock, requestMock } from '../__mocks__'; import { deleteRulesBulkRoute } from './delete_rules_bulk_route'; import { loggingSystemMock } from '@kbn/core/server/mocks'; -describe.each([ - ['Legacy', false], - ['RAC', true], -])('delete_rules - %s', (_, isRuleRegistryEnabled) => { +describe('delete_rules', () => { let server: ReturnType; let { clients, context } = requestContextMock.createTools(); @@ -31,49 +28,70 @@ describe.each([ ({ clients, context } = requestContextMock.createTools()); const logger = loggingSystemMock.createLogger(); - clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled)); // rule exists + clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); // rule exists clients.rulesClient.delete.mockResolvedValue({}); // successful deletion clients.savedObjectsClient.find.mockResolvedValue(getEmptySavedObjectsResponse()); // rule status request - deleteRulesBulkRoute(server.router, isRuleRegistryEnabled, logger); + deleteRulesBulkRoute(server.router, logger); }); describe('status codes with actionClient and alertClient', () => { test('returns 200 when deleting a single rule with a valid actionClient and alertClient by alertId', async () => { - const response = await server.inject(getDeleteBulkRequest(), context); + const response = await server.inject( + getDeleteBulkRequest(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(200); }); test('returns 200 when deleting a single rule and related rule status', async () => { - const response = await server.inject(getDeleteBulkRequest(), context); + const response = await server.inject( + getDeleteBulkRequest(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(200); }); test('returns 200 when deleting a single rule with a valid actionClient and alertClient by alertId using POST', async () => { - const response = await server.inject(getDeleteAsPostBulkRequest(), context); + const response = await server.inject( + getDeleteAsPostBulkRequest(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(200); }); test('returns 200 when deleting a single rule with a valid actionClient and alertClient by id', async () => { - const response = await server.inject(getDeleteBulkRequestById(), context); + const response = await server.inject( + getDeleteBulkRequestById(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(200); }); test('returns 200 when deleting a single rule with a valid actionClient and alertClient by id using POST', async () => { - const response = await server.inject(getDeleteAsPostBulkRequestById(), context); + const response = await server.inject( + getDeleteAsPostBulkRequestById(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(200); }); test('returns 200 because the error is in the payload when deleting a single rule that does not exist with a valid actionClient and alertClient', async () => { clients.rulesClient.find.mockResolvedValue(getEmptyFindResult()); - const response = await server.inject(getDeleteBulkRequest(), context); + const response = await server.inject( + getDeleteBulkRequest(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(200); }); test('returns 404 in the payload when deleting a single rule that does not exist with a valid actionClient and alertClient', async () => { clients.rulesClient.find.mockResolvedValue(getEmptyFindResult()); - const response = await server.inject(getDeleteBulkRequest(), context); + const response = await server.inject( + getDeleteBulkRequest(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(200); expect(response.body).toEqual( expect.arrayContaining([ @@ -93,7 +111,7 @@ describe.each([ path: DETECTION_ENGINE_RULES_BULK_DELETE, body: [{}], }); - const response = await server.inject(request, context); + const response = await server.inject(request, requestContextMock.convertContext(context)); expect(response.status).toEqual(200); expect(response.body).toEqual([ { @@ -109,7 +127,7 @@ describe.each([ path: DETECTION_ENGINE_RULES_BULK_DELETE, body: [{ id: 'c1e1b359-7ac1-4e96-bc81-c683c092436f', rule_id: 'rule_1' }], }); - const response = await server.inject(request, context); + const response = await server.inject(request, requestContextMock.convertContext(context)); expect(response.status).toEqual(200); expect(response.body).toEqual([ { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.ts index 674427a0b922..273d65a5f021 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.ts @@ -40,11 +40,7 @@ type Handler = RequestHandler< /** * @deprecated since version 8.2.0. Use the detection_engine/rules/_bulk_action API instead */ -export const deleteRulesBulkRoute = ( - router: SecuritySolutionPluginRouter, - isRuleRegistryEnabled: boolean, - logger: Logger -) => { +export const deleteRulesBulkRoute = (router: SecuritySolutionPluginRouter, logger: Logger) => { const config: Config = { validate: { body: buildRouteValidation( @@ -60,9 +56,12 @@ export const deleteRulesBulkRoute = ( logDeprecatedBulkEndpoint(logger, DETECTION_ENGINE_RULES_BULK_DELETE); const siemResponse = buildSiemResponse(response); - const rulesClient = context.alerting.getRulesClient(); - const ruleExecutionLog = context.securitySolution.getRuleExecutionLog(); - const savedObjectsClient = context.core.savedObjects.client; + + const ctx = await context.resolve(['core', 'securitySolution', 'alerting']); + + const rulesClient = ctx.alerting.getRulesClient(); + const ruleExecutionLog = ctx.securitySolution.getRuleExecutionLog(); + const savedObjectsClient = ctx.core.savedObjects.client; const rules = await Promise.all( request.body.map(async (payloadRule) => { @@ -78,7 +77,7 @@ export const deleteRulesBulkRoute = ( } try { - const rule = await readRules({ rulesClient, id, ruleId, isRuleRegistryEnabled }); + const rule = await readRules({ rulesClient, id, ruleId }); const migratedRule = await legacyMigrate({ rulesClient, savedObjectsClient, @@ -99,8 +98,7 @@ export const deleteRulesBulkRoute = ( return transformValidateBulkError( idOrRuleIdOrUnknown, migratedRule, - ruleExecutionSummary, - isRuleRegistryEnabled + ruleExecutionSummary ); } catch (err) { return transformBulkError(idOrRuleIdOrUnknown, err); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_route.test.ts index b18e330eab5a..83a40005d014 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_route.test.ts @@ -8,7 +8,7 @@ import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; import { getEmptyFindResult, - resolveAlertMock, + resolveRuleMock, getDeleteRequest, getFindResultWithSingleHit, getDeleteRequestById, @@ -18,10 +18,7 @@ import { requestContextMock, serverMock, requestMock } from '../__mocks__'; import { deleteRulesRoute } from './delete_rules_route'; import { getQueryRuleParams } from '../../schemas/rule_schemas.mock'; -describe.each([ - ['Legacy', false], - ['RAC', true], -])('delete_rules - %s', (_, isRuleRegistryEnabled) => { +describe('delete_rules', () => { let server: ReturnType; let { clients, context } = requestContextMock.createTools(); @@ -29,31 +26,38 @@ describe.each([ server = serverMock.create(); ({ clients, context } = requestContextMock.createTools()); - clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled)); + clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); clients.savedObjectsClient.find.mockResolvedValue(getEmptySavedObjectsResponse()); - deleteRulesRoute(server.router, isRuleRegistryEnabled); + deleteRulesRoute(server.router); }); describe('status codes with actionClient and alertClient', () => { test('returns 200 when deleting a single rule with a valid actionClient and alertClient by alertId', async () => { - const response = await server.inject(getDeleteRequest(), context); + const response = await server.inject( + getDeleteRequest(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(200); }); test('returns 200 when deleting a single rule with a valid actionClient and alertClient by id', async () => { - clients.rulesClient.resolve.mockResolvedValue( - resolveAlertMock(isRuleRegistryEnabled, getQueryRuleParams()) + clients.rulesClient.resolve.mockResolvedValue(resolveRuleMock(getQueryRuleParams())); + const response = await server.inject( + getDeleteRequestById(), + requestContextMock.convertContext(context) ); - const response = await server.inject(getDeleteRequestById(), context); expect(response.status).toEqual(200); }); test('returns 404 when deleting a single rule that does not exist with a valid actionClient and alertClient', async () => { clients.rulesClient.find.mockResolvedValue(getEmptyFindResult()); - const response = await server.inject(getDeleteRequest(), context); + const response = await server.inject( + getDeleteRequest(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(404); expect(response.body).toEqual({ @@ -66,7 +70,10 @@ describe.each([ clients.rulesClient.delete.mockImplementation(async () => { throw new Error('Test error'); }); - const response = await server.inject(getDeleteRequest(), context); + const response = await server.inject( + getDeleteRequest(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(500); expect(response.body).toEqual({ message: 'Test error', @@ -82,7 +89,7 @@ describe.each([ path: DETECTION_ENGINE_RULES_URL, query: {}, }); - const response = await server.inject(request, context); + const response = await server.inject(request, requestContextMock.convertContext(context)); expect(response.status).toEqual(400); expect(response.body).toEqual({ message: ['either "id" or "rule_id" must be set'], diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_route.ts index 5e2382cfe640..beb2cf94c047 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_route.ts @@ -21,10 +21,7 @@ import { buildSiemResponse } from '../utils'; import { readRules } from '../../rules/read_rules'; import { legacyMigrate } from '../../rules/utils'; -export const deleteRulesRoute = ( - router: SecuritySolutionPluginRouter, - isRuleRegistryEnabled: boolean -) => { +export const deleteRulesRoute = (router: SecuritySolutionPluginRouter) => { router.delete( { path: DETECTION_ENGINE_RULES_URL, @@ -47,11 +44,12 @@ export const deleteRulesRoute = ( try { const { id, rule_id: ruleId } = request.query; - const rulesClient = context.alerting.getRulesClient(); - const ruleExecutionLog = context.securitySolution.getRuleExecutionLog(); - const savedObjectsClient = context.core.savedObjects.client; + const ctx = await context.resolve(['core', 'securitySolution', 'alerting']); + const rulesClient = ctx.alerting.getRulesClient(); + const ruleExecutionLog = ctx.securitySolution.getRuleExecutionLog(); + const savedObjectsClient = ctx.core.savedObjects.client; - const rule = await readRules({ isRuleRegistryEnabled, rulesClient, id, ruleId }); + const rule = await readRules({ rulesClient, id, ruleId }); const migratedRule = await legacyMigrate({ rulesClient, savedObjectsClient, @@ -74,7 +72,7 @@ export const deleteRulesRoute = ( ruleExecutionLog, }); - const transformed = transform(migratedRule, ruleExecutionSummary, isRuleRegistryEnabled); + const transformed = transform(migratedRule, ruleExecutionSummary); if (transformed == null) { return siemResponse.error({ statusCode: 500, body: 'failed to transform alert' }); } else { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/export_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/export_rules_route.ts index de66a6e5a9d9..f21df4556327 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/export_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/export_rules_route.ts @@ -25,8 +25,7 @@ import { buildSiemResponse } from '../utils'; export const exportRulesRoute = ( router: SecuritySolutionPluginRouter, config: ConfigType, - logger: Logger, - isRuleRegistryEnabled: boolean + logger: Logger ) => { router.post( { @@ -45,9 +44,9 @@ export const exportRulesRoute = ( }, async (context, request, response) => { const siemResponse = buildSiemResponse(response); - const rulesClient = context.alerting.getRulesClient(); - const exceptionsClient = context.lists?.getExceptionListClient(); - const savedObjectsClient = context.core.savedObjects.client; + const rulesClient = (await context.alerting).getRulesClient(); + const exceptionsClient = (await context.lists)?.getExceptionListClient(); + const savedObjectsClient = (await context.core).savedObjects.client; try { const exportSizeLimit = config.maxRuleImportExportSize; @@ -58,7 +57,6 @@ export const exportRulesRoute = ( }); } else { const nonPackagedRulesCount = await getNonPackagedRulesCount({ - isRuleRegistryEnabled, rulesClient, }); if (nonPackagedRulesCount > exportSizeLimit) { @@ -76,16 +74,9 @@ export const exportRulesRoute = ( exceptionsClient, savedObjectsClient, request.body.objects, - logger, - isRuleRegistryEnabled + logger ) - : await getExportAll( - rulesClient, - exceptionsClient, - savedObjectsClient, - logger, - isRuleRegistryEnabled - ); + : await getExportAll(rulesClient, exceptionsClient, savedObjectsClient, logger); const responseBody = request.query.exclude_export_details ? exportedRulesAndExceptions.rulesNdjson diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rules_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rules_route.test.ts index 6183bc78b916..b4aada2a60b6 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rules_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rules_route.test.ts @@ -10,7 +10,7 @@ import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; import { getQueryRuleParams } from '../../schemas/rule_schemas.mock'; import { requestContextMock, requestMock, serverMock } from '../__mocks__'; import { - getAlertMock, + getRuleMock, getFindRequest, getFindResultWithSingleHit, getEmptySavedObjectsResponse, @@ -18,10 +18,7 @@ import { } from '../__mocks__/request_responses'; import { findRulesRoute } from './find_rules_route'; -describe.each([ - ['Legacy', false], - ['RAC', true], -])('find_rules - %s', (_, isRuleRegistryEnabled) => { +describe('find_rules', () => { let server: ReturnType; let { clients, context } = requestContextMock.createTools(); let logger: ReturnType; @@ -31,21 +28,22 @@ describe.each([ logger = loggingSystemMock.createLogger(); ({ clients, context } = requestContextMock.createTools()); - clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled)); - clients.rulesClient.get.mockResolvedValue( - getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()) - ); + clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); + clients.rulesClient.get.mockResolvedValue(getRuleMock(getQueryRuleParams())); clients.savedObjectsClient.find.mockResolvedValue(getEmptySavedObjectsResponse()); clients.ruleExecutionLog.getExecutionSummariesBulk.mockResolvedValue( getRuleExecutionSummaries() ); - findRulesRoute(server.router, logger, isRuleRegistryEnabled); + findRulesRoute(server.router, logger); }); describe('status codes', () => { test('returns 200', async () => { - const response = await server.inject(getFindRequest(), context); + const response = await server.inject( + getFindRequest(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(200); }); @@ -53,7 +51,10 @@ describe.each([ clients.rulesClient.find.mockImplementation(async () => { throw new Error('Test error'); }); - const response = await server.inject(getFindRequest(), context); + const response = await server.inject( + getFindRequest(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(500); expect(response.body).toEqual({ message: 'Test error', diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rules_route.ts index e3749888c2d8..eac8c4a5001d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rules_route.ts @@ -22,11 +22,7 @@ import { transformFindAlerts } from './utils'; // eslint-disable-next-line no-restricted-imports import { legacyGetBulkRuleActionsSavedObject } from '../../rule_actions/legacy_get_bulk_rule_actions_saved_object'; -export const findRulesRoute = ( - router: SecuritySolutionPluginRouter, - logger: Logger, - isRuleRegistryEnabled: boolean -) => { +export const findRulesRoute = (router: SecuritySolutionPluginRouter, logger: Logger) => { router.get( { path: `${DETECTION_ENGINE_RULES_URL}/_find`, @@ -49,12 +45,12 @@ export const findRulesRoute = ( try { const { query } = request; - const rulesClient = context.alerting.getRulesClient(); - const ruleExecutionLog = context.securitySolution.getRuleExecutionLog(); - const savedObjectsClient = context.core.savedObjects.client; + const ctx = await context.resolve(['core', 'securitySolution', 'alerting']); + const rulesClient = ctx.alerting.getRulesClient(); + const ruleExecutionLog = ctx.securitySolution.getRuleExecutionLog(); + const savedObjectsClient = ctx.core.savedObjects.client; const rules = await findRules({ - isRuleRegistryEnabled, rulesClient, perPage: query.per_page, page: query.page, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.test.ts index 10be2ac921c1..2883d9af0f9e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.test.ts @@ -51,10 +51,7 @@ jest.mock('../../../timeline/utils/check_timelines_status', () => { }; }); -describe.each([ - ['Legacy', false], - ['RAC', true], -])('get_prepackaged_rule_status_route - %s', (_, isRuleRegistryEnabled) => { +describe('get_prepackaged_rule_status_route', () => { const mockGetCurrentUser = { user: { username: 'mockUser', @@ -85,17 +82,15 @@ describe.each([ prepackagedTimelines: [], }); - getPrepackagedRulesStatusRoute( - server.router, - createMockConfig(), - securitySetup, - isRuleRegistryEnabled - ); + getPrepackagedRulesStatusRoute(server.router, createMockConfig(), securitySetup); }); describe('status codes', () => { test('returns 200', async () => { - const response = await server.inject(getPrepackagedRulesStatusRequest(), context); + const response = await server.inject( + getPrepackagedRulesStatusRequest(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(200); }); @@ -103,7 +98,10 @@ describe.each([ clients.rulesClient.find.mockImplementation(async () => { throw new Error('Test error'); }); - const response = await server.inject(getPrepackagedRulesStatusRequest(), context); + const response = await server.inject( + getPrepackagedRulesStatusRequest(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(500); expect(response.body).toEqual({ message: 'Test error', @@ -116,7 +114,7 @@ describe.each([ test('0 rules installed, 0 custom rules, 1 rules not installed, and 1 rule not updated', async () => { clients.rulesClient.find.mockResolvedValue(getEmptyFindResult()); const request = getPrepackagedRulesStatusRequest(); - const response = await server.inject(request, context); + const response = await server.inject(request, requestContextMock.convertContext(context)); expect(response.status).toEqual(200); expect(response.body).toEqual({ @@ -131,9 +129,9 @@ describe.each([ }); test('1 rule installed, 1 custom rules, 0 rules not installed, and 1 rule to not updated', async () => { - clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled)); + clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); const request = getPrepackagedRulesStatusRequest(); - const response = await server.inject(request, context); + const response = await server.inject(request, requestContextMock.convertContext(context)); expect(response.status).toEqual(200); expect(response.body).toEqual({ @@ -153,7 +151,7 @@ describe.each([ mockCheckTimelinesStatusBeforeInstallResult ); const request = getPrepackagedRulesStatusRequest(); - const response = await server.inject(request, context); + const response = await server.inject(request, requestContextMock.convertContext(context)); expect(response.status).toEqual(200); expect(response.body).toEqual({ @@ -173,7 +171,7 @@ describe.each([ mockCheckTimelinesStatusAfterInstallResult ); const request = getPrepackagedRulesStatusRequest(); - const response = await server.inject(request, context); + const response = await server.inject(request, requestContextMock.convertContext(context)); expect(response.status).toEqual(200); expect(response.body).toEqual({ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.ts index 4fcf1bd992c6..d35df4d51ee0 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.ts @@ -32,8 +32,7 @@ import { export const getPrepackagedRulesStatusRoute = ( router: SecuritySolutionPluginRouter, config: ConfigType, - security: SetupPlugins['security'], - isRuleRegistryEnabled: boolean + security: SetupPlugins['security'] ) => { router.get( { @@ -45,8 +44,9 @@ export const getPrepackagedRulesStatusRoute = ( }, async (context, request, response) => { const siemResponse = buildSiemResponse(response); - const savedObjectsClient = context.core.savedObjects.client; - const rulesClient = context.alerting.getRulesClient(); + const ctx = await context.resolve(['core', 'alerting']); + const savedObjectsClient = ctx.core.savedObjects.client; + const rulesClient = ctx.alerting.getRulesClient(); const ruleAssetsClient = ruleAssetSavedObjectsClientFactory(savedObjectsClient); try { @@ -56,7 +56,6 @@ export const getPrepackagedRulesStatusRoute = ( config.prebuiltRulesFromSavedObjects ); const customRules = await findRules({ - isRuleRegistryEnabled, rulesClient, perPage: 1, page: 1, @@ -68,7 +67,6 @@ export const getPrepackagedRulesStatusRoute = ( const frameworkRequest = await buildFrameworkRequest(context, security, request); const prepackagedRules = await getExistingPrepackagedRules({ rulesClient, - isRuleRegistryEnabled, }); const rulesToInstall = getRulesToInstall(latestPrepackagedRules, prepackagedRules); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/get_rule_execution_events_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/get_rule_execution_events_route.test.ts index b24789d77a9b..37b1f4132867 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/get_rule_execution_events_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/get_rule_execution_events_route.test.ts @@ -30,7 +30,10 @@ describe('getRuleExecutionEventsRoute', () => { const executionEvents = getAggregateExecutionEvents(); clients.ruleExecutionLog.getAggregateExecutionEvents.mockResolvedValue(executionEvents); - const response = await server.inject(getRuleExecutionEventsRequest(), context); + const response = await server.inject( + getRuleExecutionEventsRequest(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(200); expect(response.body).toEqual(executionEvents); @@ -41,7 +44,10 @@ describe('getRuleExecutionEventsRoute', () => { it('returns 500 response with it', async () => { clients.ruleExecutionLog.getAggregateExecutionEvents.mockRejectedValue(new Error('Boom!')); - const response = await server.inject(getRuleExecutionEventsRequest(), context); + const response = await server.inject( + getRuleExecutionEventsRequest(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(500); expect(response.body).toEqual({ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/get_rule_execution_events_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/get_rule_execution_events_route.ts index 76088cd07b80..6bd6610c9c91 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/get_rule_execution_events_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/get_rule_execution_events_route.ts @@ -48,7 +48,7 @@ export const getRuleExecutionEventsRoute = (router: SecuritySolutionPluginRouter const siemResponse = buildSiemResponse(response); try { - const executionLog = context.securitySolution.getRuleExecutionLog(); + const executionLog = (await context.securitySolution).getRuleExecutionLog(); const { events, total } = await executionLog.getAggregateExecutionEvents({ ruleId, start, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/import_rules_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/import_rules_route.test.ts index 63c25da1c2d4..c2aea239f656 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/import_rules_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/import_rules_route.test.ts @@ -10,10 +10,9 @@ import { getImportRulesRequest, getImportRulesRequestOverwriteTrue, getEmptyFindResult, - getAlertMock, + getRuleMock, getFindResultWithSingleHit, getBasicEmptySearchResponse, - getBasicNoShardsSearchResponse, } from '../__mocks__/request_responses'; import { createMockConfig, requestContextMock, serverMock, requestMock } from '../__mocks__'; import { mlServicesMock, mlAuthzMock as mockMlAuthzFactory } from '../../../machine_learning/mocks'; @@ -31,10 +30,7 @@ import { getQueryRuleParams } from '../../schemas/rule_schemas.mock'; jest.mock('../../../machine_learning/authz', () => mockMlAuthzFactory.create()); -describe.each([ - ['Legacy', false], - ['RAC', true], -])('import_rules_route - %s', (_, isRuleRegistryEnabled) => { +describe('import_rules_route', () => { let config: ReturnType; let server: ReturnType; let request: ReturnType; @@ -50,19 +46,17 @@ describe.each([ ml = mlServicesMock.createSetupContract(); clients.rulesClient.find.mockResolvedValue(getEmptyFindResult()); // no extant rules - clients.rulesClient.update.mockResolvedValue( - getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()) - ); + clients.rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); clients.actionsClient.getAll.mockResolvedValue([]); context.core.elasticsearch.client.asCurrentUser.search.mockResolvedValue( elasticsearchClientMock.createSuccessTransportRequestPromise(getBasicEmptySearchResponse()) ); - importRulesRoute(server.router, config, ml, isRuleRegistryEnabled); + importRulesRoute(server.router, config, ml); }); describe('status codes', () => { test('returns 200 when importing a single rule with a valid actionClient and alertClient', async () => { - const response = await server.inject(request, context); + const response = await server.inject(request, requestContextMock.convertContext(context)); expect(response.status).toEqual(200); }); @@ -70,7 +64,10 @@ describe.each([ test('returns 500 if more than 10,000 rules are imported', async () => { const ruleIds = new Array(10001).fill(undefined).map((__, index) => `rule-${index}`); const multiRequest = getImportRulesRequest(buildHapiStream(ruleIdsToNdJsonString(ruleIds))); - const response = await server.inject(multiRequest, context); + const response = await server.inject( + multiRequest, + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(500); expect(response.body).toEqual({ @@ -81,14 +78,14 @@ describe.each([ }); describe('unhappy paths', () => { - it('returns a 403 error object if ML Authz fails', async () => { + test('returns a 403 error object if ML Authz fails', async () => { (buildMlAuthz as jest.Mock).mockReturnValueOnce({ validateRuleType: jest .fn() .mockResolvedValue({ valid: false, message: 'mocked validation message' }), }); - const response = await server.inject(request, context); + const response = await server.inject(request, requestContextMock.convertContext(context)); expect(response.status).toEqual(200); expect(response.body).toEqual({ errors: [ @@ -114,50 +111,17 @@ describe.each([ .mockImplementation(() => { throw new Error('Test error'); }); - const response = await server.inject(request, context); + const response = await server.inject(request, requestContextMock.convertContext(context)); expect(response.status).toEqual(500); expect(response.body).toEqual({ message: 'Test error', status_code: 500 }); transformMock.mockRestore(); }); - test('returns an error if the index does not exist when rule registry not enabled', async () => { - clients.appClient.getSignalsIndex.mockReturnValue('mockSignalsIndex'); - context.core.elasticsearch.client.asCurrentUser.search.mockResolvedValueOnce( - elasticsearchClientMock.createSuccessTransportRequestPromise( - getBasicNoShardsSearchResponse() - ) - ); - const response = await server.inject(request, context); - expect(response.status).toEqual(isRuleRegistryEnabled ? 200 : 400); - if (!isRuleRegistryEnabled) { - expect(response.body).toEqual({ - message: - 'To create a rule, the index must exist first. Index mockSignalsIndex does not exist', - status_code: 400, - }); - } - }); - - test('returns an error when cluster throws error', async () => { - context.core.elasticsearch.client.asCurrentUser.search.mockResolvedValue( - elasticsearchClientMock.createErrorTransportRequestPromise({ - body: new Error('Test error'), - }) - ); - - const response = await server.inject(request, context); - expect(response.status).toEqual(500); - expect(response.body).toEqual({ - message: 'Test error', - status_code: 500, - }); - }); - test('returns 400 if file extension type is not .ndjson', async () => { const requestPayload = buildHapiStream(ruleIdsToNdJsonString(['rule-1']), 'wrong.html'); const badRequest = getImportRulesRequest(requestPayload); - const response = await server.inject(badRequest, context); + const response = await server.inject(badRequest, requestContextMock.convertContext(context)); expect(response.status).toEqual(400); expect(response.body).toEqual({ message: 'Invalid file extension .html', status_code: 400 }); @@ -166,10 +130,8 @@ describe.each([ describe('single rule import', () => { test('returns 200 if rule imported successfully', async () => { - clients.rulesClient.create.mockResolvedValue( - getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()) - ); - const response = await server.inject(request, context); + clients.rulesClient.create.mockResolvedValue(getRuleMock(getQueryRuleParams())); + const response = await server.inject(request, requestContextMock.convertContext(context)); expect(response.status).toEqual(200); expect(response.body).toEqual({ errors: [], @@ -184,7 +146,7 @@ describe.each([ test('returns reported conflict if error parsing rule', async () => { const requestPayload = buildHapiStream('this is not a valid ndjson string!'); const badRequest = getImportRulesRequest(requestPayload); - const response = await server.inject(badRequest, context); + const response = await server.inject(badRequest, requestContextMock.convertContext(context)); expect(response.status).toEqual(200); expect(response.body).toEqual({ @@ -207,10 +169,8 @@ describe.each([ describe('rule with existing rule_id', () => { test('returns with reported conflict if `overwrite` is set to `false`', async () => { - clients.rulesClient.find.mockResolvedValue( - getFindResultWithSingleHit(isRuleRegistryEnabled) - ); // extant rule - const response = await server.inject(request, context); + clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); // extant rule + const response = await server.inject(request, requestContextMock.convertContext(context)); expect(response.status).toEqual(200); expect(response.body).toEqual({ @@ -232,13 +192,14 @@ describe.each([ }); test('returns with NO reported conflict if `overwrite` is set to `true`', async () => { - clients.rulesClient.find.mockResolvedValue( - getFindResultWithSingleHit(isRuleRegistryEnabled) - ); // extant rule + clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); // extant rule const overwriteRequest = getImportRulesRequestOverwriteTrue( buildHapiStream(ruleIdsToNdJsonString(['rule-1'])) ); - const response = await server.inject(overwriteRequest, context); + const response = await server.inject( + overwriteRequest, + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(200); expect(response.body).toEqual({ @@ -258,7 +219,10 @@ describe.each([ const multiRequest = getImportRulesRequest( buildHapiStream(ruleIdsToNdJsonString(['rule-1', 'rule-2'])) ); - const response = await server.inject(multiRequest, context); + const response = await server.inject( + multiRequest, + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(200); expect(response.body).toEqual({ @@ -274,7 +238,10 @@ describe.each([ test('returns 200 if many rules are imported successfully', async () => { const ruleIds = new Array(9999).fill(undefined).map((__, index) => `rule-${index}`); const multiRequest = getImportRulesRequest(buildHapiStream(ruleIdsToNdJsonString(ruleIds))); - const response = await server.inject(multiRequest, context); + const response = await server.inject( + multiRequest, + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(200); expect(response.body).toEqual({ @@ -298,7 +265,7 @@ describe.each([ const badPayload = buildHapiStream(rulesToNdJsonString(rulesWithoutRuleIds)); const badRequest = getImportRulesRequest(badPayload); - const response = await server.inject(badRequest, context); + const response = await server.inject(badRequest, requestContextMock.convertContext(context)); expect(response.status).toEqual(200); expect(response.body).toEqual({ @@ -331,7 +298,10 @@ describe.each([ const multiRequest = getImportRulesRequest( buildHapiStream(ruleIdsToNdJsonString(['rule-1', 'rule-1'])) ); - const response = await server.inject(multiRequest, context); + const response = await server.inject( + multiRequest, + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(200); expect(response.body).toEqual({ @@ -357,7 +327,10 @@ describe.each([ buildHapiStream(ruleIdsToNdJsonString(['rule-1', 'rule-1'])) ); - const response = await server.inject(multiRequest, context); + const response = await server.inject( + multiRequest, + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(200); expect(response.body).toEqual({ errors: [], @@ -372,16 +345,17 @@ describe.each([ describe('rules with existing rule_id', () => { beforeEach(() => { - clients.rulesClient.find.mockResolvedValueOnce( - getFindResultWithSingleHit(isRuleRegistryEnabled) - ); // extant rule + clients.rulesClient.find.mockResolvedValueOnce(getFindResultWithSingleHit()); // extant rule }); test('returns with reported conflict if `overwrite` is set to `false`', async () => { const multiRequest = getImportRulesRequest( buildHapiStream(ruleIdsToNdJsonString(['rule-1', 'rule-2', 'rule-3'])) ); - const response = await server.inject(multiRequest, context); + const response = await server.inject( + multiRequest, + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(200); expect(response.body).toEqual({ errors: [ @@ -405,7 +379,10 @@ describe.each([ const multiRequest = getImportRulesRequestOverwriteTrue( buildHapiStream(ruleIdsToNdJsonString(['rule-1', 'rule-2', 'rule-3'])) ); - const response = await server.inject(multiRequest, context); + const response = await server.inject( + multiRequest, + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(200); expect(response.body).toEqual({ errors: [], diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/import_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/import_rules_route.ts index b0f09a8c76d3..2a344cfb7630 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/import_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/import_rules_route.ts @@ -10,7 +10,7 @@ import { extname } from 'path'; import { schema } from '@kbn/config-schema'; import { createPromiseFromStreams } from '@kbn/utils'; -import { transformError, getIndexExists } from '@kbn/securitysolution-es-utils'; +import { transformError } from '@kbn/securitysolution-es-utils'; import { validate } from '@kbn/securitysolution-io-ts-utils'; import { ImportQuerySchemaDecoded, importQuerySchema } from '@kbn/securitysolution-io-ts-types'; @@ -52,8 +52,7 @@ const CHUNK_PARSED_OBJECT_SIZE = 50; export const importRulesRoute = ( router: SecuritySolutionPluginRouter, config: ConfigType, - ml: SetupPlugins['ml'], - isRuleRegistryEnabled: boolean + ml: SetupPlugins['ml'] ) => { router.post( { @@ -76,18 +75,25 @@ export const importRulesRoute = ( const siemResponse = buildSiemResponse(response); try { - const rulesClient = context.alerting.getRulesClient(); - const actionsClient = context.actions.getActionsClient(); - const esClient = context.core.elasticsearch.client; - const actionSOClient = context.core.savedObjects.getClient({ + const ctx = await context.resolve([ + 'core', + 'securitySolution', + 'alerting', + 'actions', + 'lists', + 'licensing', + ]); + + const rulesClient = ctx.alerting.getRulesClient(); + const actionsClient = ctx.actions.getActionsClient(); + const actionSOClient = ctx.core.savedObjects.getClient({ includedHiddenTypes: ['action'], }); - const savedObjectsClient = context.core.savedObjects.client; - const siemClient = context.securitySolution.getAppClient(); - const exceptionsClient = context.lists?.getExceptionListClient(); + const savedObjectsClient = ctx.core.savedObjects.client; + const exceptionsClient = ctx.lists?.getExceptionListClient(); const mlAuthz = buildMlAuthz({ - license: context.licensing.license, + license: ctx.licensing.license, ml, request, savedObjectsClient, @@ -102,14 +108,6 @@ export const importRulesRoute = ( }); } - const signalsIndex = siemClient.getSignalsIndex(); - const indexExists = await getIndexExists(esClient.asCurrentUser, signalsIndex); - if (!isRuleRegistryEnabled && !indexExists) { - return siemResponse.error({ - statusCode: 400, - body: `To create a rule, the index must exist first. Index ${signalsIndex} does not exist`, - }); - } const objectLimit = config.maxRuleImportExportSize; // parse file to separate out exceptions from rules @@ -171,9 +169,7 @@ export const importRulesRoute = ( rulesClient, savedObjectsClient, exceptionsClient, - isRuleRegistryEnabled, - spaceId: context.securitySolution.getSpaceId(), - signalsIndex, + spaceId: ctx.securitySolution.getSpaceId(), existingLists: foundReferencedExceptionLists, }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/legacy_create_legacy_notification.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/legacy_create_legacy_notification.ts index 728c5c479007..6b6e65329c06 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/legacy_create_legacy_notification.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/legacy_create_legacy_notification.ts @@ -56,8 +56,8 @@ export const legacyCreateLegacyNotificationRoute = ( }, }, async (context, request, response) => { - const rulesClient = context.alerting.getRulesClient(); - const savedObjectsClient = context.core.savedObjects.client; + const rulesClient = (await context.alerting).getRulesClient(); + const savedObjectsClient = (await context.core).savedObjects.client; const { alert_id: ruleAlertId } = request.query; const { actions, interval, name } = request.body; try { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.test.ts index 8285c2dad45a..6abac6e94638 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.test.ts @@ -15,7 +15,7 @@ import { getEmptyFindResult, getFindResultWithSingleHit, getPatchBulkRequest, - getAlertMock, + getRuleMock, typicalMlRulePayload, } from '../__mocks__/request_responses'; import { serverMock, requestContextMock, requestMock } from '../__mocks__'; @@ -26,10 +26,7 @@ import { loggingSystemMock } from '@kbn/core/server/mocks'; jest.mock('../../../machine_learning/authz', () => mockMlAuthzFactory.create()); -describe.each([ - ['Legacy', false], - ['RAC', true], -])('patch_rules_bulk - %s', (_, isRuleRegistryEnabled) => { +describe('patch_rules_bulk', () => { let server: ReturnType; let { clients, context } = requestContextMock.createTools(); let ml: ReturnType; @@ -40,23 +37,27 @@ describe.each([ ml = mlServicesMock.createSetupContract(); const logger = loggingSystemMock.createLogger(); - clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled)); // rule exists - clients.rulesClient.update.mockResolvedValue( - getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()) - ); // update succeeds + clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); // rule exists + clients.rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); // update succeeds - patchRulesBulkRoute(server.router, ml, isRuleRegistryEnabled, logger); + patchRulesBulkRoute(server.router, ml, logger); }); describe('status codes', () => { test('returns 200', async () => { - const response = await server.inject(getPatchBulkRequest(), context); + const response = await server.inject( + getPatchBulkRequest(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(200); }); test('returns an error in the response when updating a single rule that does not exist', async () => { clients.rulesClient.find.mockResolvedValue(getEmptyFindResult()); - const response = await server.inject(getPatchBulkRequest(), context); + const response = await server.inject( + getPatchBulkRequest(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(200); expect(response.body).toEqual([ { @@ -79,7 +80,7 @@ describe.each([ }, ], }); - await server.inject(request, context); + await server.inject(request, requestContextMock.convertContext(context)); expect(clients.rulesClient.update).toHaveBeenCalledWith( expect.objectContaining({ @@ -104,7 +105,7 @@ describe.each([ path: DETECTION_ENGINE_RULES_BULK_UPDATE, body: [typicalMlRulePayload()], }); - const response = await server.inject(request, context); + const response = await server.inject(request, requestContextMock.convertContext(context)); expect(response.status).toEqual(200); expect(response.body).toEqual([ @@ -130,7 +131,7 @@ describe.each([ path: DETECTION_ENGINE_RULES_BULK_UPDATE, body: [payloadWithoutType], }); - const response = await server.inject(request, context); + const response = await server.inject(request, requestContextMock.convertContext(context)); expect(response.status).toEqual(200); expect(response.body).toEqual([ @@ -152,7 +153,7 @@ describe.each([ path: DETECTION_ENGINE_RULES_BULK_UPDATE, body: [{ ...getCreateRulesSchemaMock(), rule_id: undefined }], }); - const response = await server.inject(request, context); + const response = await server.inject(request, requestContextMock.convertContext(context)); expect(response.status).toEqual(200); expect(response.body).toEqual([ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts index a0ed8abb8b9a..ea6699947692 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts @@ -34,7 +34,6 @@ import { getDeprecatedBulkEndpointHeader, logDeprecatedBulkEndpoint } from './ut export const patchRulesBulkRoute = ( router: SecuritySolutionPluginRouter, ml: SetupPlugins['ml'], - isRuleRegistryEnabled: boolean, logger: Logger ) => { router.patch( @@ -54,12 +53,14 @@ export const patchRulesBulkRoute = ( const siemResponse = buildSiemResponse(response); - const rulesClient = context.alerting.getRulesClient(); - const ruleExecutionLog = context.securitySolution.getRuleExecutionLog(); - const savedObjectsClient = context.core.savedObjects.client; + const ctx = await context.resolve(['core', 'securitySolution', 'alerting', 'licensing']); + + const rulesClient = ctx.alerting.getRulesClient(); + const ruleExecutionLog = ctx.securitySolution.getRuleExecutionLog(); + const savedObjectsClient = ctx.core.savedObjects.client; const mlAuthz = buildMlAuthz({ - license: context.licensing.license, + license: ctx.licensing.license, ml, request, savedObjectsClient, @@ -129,7 +130,6 @@ export const patchRulesBulkRoute = ( } const existingRule = await readRules({ - isRuleRegistryEnabled, rulesClient, ruleId, id, @@ -198,12 +198,7 @@ export const patchRulesBulkRoute = ( }); if (rule != null && rule.enabled != null && rule.name != null) { const ruleExecutionSummary = await ruleExecutionLog.getExecutionSummary(rule.id); - return transformValidateBulkError( - rule.id, - rule, - ruleExecutionSummary, - isRuleRegistryEnabled - ); + return transformValidateBulkError(rule.id, rule, ruleExecutionSummary); } else { return getIdBulkError({ id, ruleId }); } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_route.test.ts index 51bced3480f2..cbcf5540b4f1 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_route.test.ts @@ -11,7 +11,7 @@ import { buildMlAuthz } from '../../../machine_learning/authz'; import { getEmptyFindResult, getRuleExecutionSummarySucceeded, - getAlertMock, + getRuleMock, getPatchRequest, getFindResultWithSingleHit, nonRuleFindResult, @@ -24,10 +24,7 @@ import { getQueryRuleParams } from '../../schemas/rule_schemas.mock'; jest.mock('../../../machine_learning/authz', () => mockMlAuthzFactory.create()); -describe.each([ - ['Legacy', false], - ['RAC', true], -])('patch_rules - %s', (_, isRuleRegistryEnabled) => { +describe('patch_rules', () => { let server: ReturnType; let { clients, context } = requestContextMock.createTools(); let ml: ReturnType; @@ -37,29 +34,31 @@ describe.each([ ({ clients, context } = requestContextMock.createTools()); ml = mlServicesMock.createSetupContract(); - clients.rulesClient.get.mockResolvedValue( - getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()) - ); // existing rule - clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled)); // existing rule - clients.rulesClient.update.mockResolvedValue( - getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()) - ); // successful update + clients.rulesClient.get.mockResolvedValue(getRuleMock(getQueryRuleParams())); // existing rule + clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); // existing rule + clients.rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); // successful update clients.ruleExecutionLog.getExecutionSummary.mockResolvedValue( getRuleExecutionSummarySucceeded() ); - patchRulesRoute(server.router, ml, isRuleRegistryEnabled); + patchRulesRoute(server.router, ml); }); describe('status codes', () => { test('returns 200', async () => { - const response = await server.inject(getPatchRequest(), context); + const response = await server.inject( + getPatchRequest(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(200); }); test('returns 404 when updating a single rule that does not exist', async () => { clients.rulesClient.find.mockResolvedValue(getEmptyFindResult()); - const response = await server.inject(getPatchRequest(), context); + const response = await server.inject( + getPatchRequest(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(404); expect(response.body).toEqual({ message: 'rule_id: "rule-1" not found', @@ -68,8 +67,11 @@ describe.each([ }); test('returns error if requesting a non-rule', async () => { - clients.rulesClient.find.mockResolvedValue(nonRuleFindResult(isRuleRegistryEnabled)); - const response = await server.inject(getPatchRequest(), context); + clients.rulesClient.find.mockResolvedValue(nonRuleFindResult()); + const response = await server.inject( + getPatchRequest(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(404); expect(response.body).toEqual({ message: expect.stringContaining('not found'), @@ -81,7 +83,10 @@ describe.each([ clients.rulesClient.update.mockImplementation(async () => { throw new Error('Test error'); }); - const response = await server.inject(getPatchRequest(), context); + const response = await server.inject( + getPatchRequest(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(500); expect(response.body).toEqual({ message: 'Test error', @@ -100,7 +105,7 @@ describe.each([ machine_learning_job_id: 'some_job_id', }, }); - await server.inject(request, context); + await server.inject(request, requestContextMock.convertContext(context)); expect(clients.rulesClient.update).toHaveBeenCalledWith( expect.objectContaining({ @@ -125,7 +130,7 @@ describe.each([ path: DETECTION_ENGINE_RULES_URL, body: typicalMlRulePayload(), }); - const response = await server.inject(request, context); + const response = await server.inject(request, requestContextMock.convertContext(context)); expect(response.status).toEqual(403); expect(response.body).toEqual({ @@ -146,7 +151,7 @@ describe.each([ path: DETECTION_ENGINE_RULES_URL, body: payloadWithoutType, }); - const response = await server.inject(request, context); + const response = await server.inject(request, requestContextMock.convertContext(context)); expect(response.status).toEqual(403); expect(response.body).toEqual({ @@ -163,7 +168,7 @@ describe.each([ path: DETECTION_ENGINE_RULES_URL, body: { ...getPatchRulesSchemaMock(), rule_id: undefined }, }); - const response = await server.inject(request, context); + const response = await server.inject(request, requestContextMock.convertContext(context)); expect(response.body).toEqual({ message: ['either "id" or "rule_id" must be set'], status_code: 400, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_route.ts index a5bc76cc5ef2..09d9b2691715 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_route.ts @@ -27,11 +27,7 @@ import { readRules } from '../../rules/read_rules'; import { legacyMigrate } from '../../rules/utils'; import { PartialFilter } from '../../types'; -export const patchRulesRoute = ( - router: SecuritySolutionPluginRouter, - ml: SetupPlugins['ml'], - isRuleRegistryEnabled: boolean -) => { +export const patchRulesRoute = (router: SecuritySolutionPluginRouter, ml: SetupPlugins['ml']) => { router.patch( { path: DETECTION_ENGINE_RULES_URL, @@ -106,12 +102,12 @@ export const patchRulesRoute = ( const actions: RuleAlertAction[] = actionsRest as RuleAlertAction[]; const filters: PartialFilter[] | undefined = filtersRest as PartialFilter[]; - const rulesClient = context.alerting.getRulesClient(); - const ruleExecutionLog = context.securitySolution.getRuleExecutionLog(); - const savedObjectsClient = context.core.savedObjects.client; + const rulesClient = (await context.alerting).getRulesClient(); + const ruleExecutionLog = (await context.securitySolution).getRuleExecutionLog(); + const savedObjectsClient = (await context.core).savedObjects.client; const mlAuthz = buildMlAuthz({ - license: context.licensing.license, + license: (await context.licensing).license, ml, request, savedObjectsClient, @@ -122,7 +118,6 @@ export const patchRulesRoute = ( } const existingRule = await readRules({ - isRuleRegistryEnabled, rulesClient, ruleId, id, @@ -192,11 +187,7 @@ export const patchRulesRoute = ( if (rule != null && rule.enabled != null && rule.name != null) { const ruleExecutionSummary = await ruleExecutionLog.getExecutionSummary(rule.id); - const [validated, errors] = transformValidate( - rule, - ruleExecutionSummary, - isRuleRegistryEnabled - ); + const [validated, errors] = transformValidate(rule, ruleExecutionSummary); if (errors != null) { return siemResponse.error({ statusCode: 500, body: errors }); } else { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/perform_bulk_action_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/perform_bulk_action_route.test.ts index e7e960e83245..3dc8ab5199c7 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/perform_bulk_action_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/perform_bulk_action_route.test.ts @@ -27,29 +27,29 @@ import { readRules } from '../../rules/read_rules'; jest.mock('../../../machine_learning/authz', () => mockMlAuthzFactory.create()); jest.mock('../../rules/read_rules', () => ({ readRules: jest.fn() })); -describe.each([ - ['Legacy', false], - ['RAC', true], -])('perform_bulk_action - %s', (_, isRuleRegistryEnabled) => { +describe('perform_bulk_action', () => { const readRulesMock = readRules as jest.Mock; let server: ReturnType; let { clients, context } = requestContextMock.createTools(); let ml: ReturnType; let logger: ReturnType; - const mockRule = getFindResultWithSingleHit(isRuleRegistryEnabled).data[0]; + const mockRule = getFindResultWithSingleHit().data[0]; beforeEach(() => { server = serverMock.create(); logger = loggingSystemMock.createLogger(); ({ clients, context } = requestContextMock.createTools()); ml = mlServicesMock.createSetupContract(); - clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled)); - performBulkActionRoute(server.router, ml, logger, isRuleRegistryEnabled); + clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); + performBulkActionRoute(server.router, ml, logger); }); describe('status codes', () => { it('returns 200 when performing bulk action with all dependencies present', async () => { - const response = await server.inject(getBulkActionRequest(), context); + const response = await server.inject( + getBulkActionRequest(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(200); expect(response.body).toEqual({ success: true, @@ -67,7 +67,10 @@ describe.each([ it("returns 200 when provided filter query doesn't match any rules", async () => { clients.rulesClient.find.mockResolvedValue(getEmptyFindResult()); - const response = await server.inject(getBulkActionRequest(), context); + const response = await server.inject( + getBulkActionRequest(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(200); expect(response.body).toEqual({ success: true, @@ -87,7 +90,10 @@ describe.each([ clients.rulesClient.find.mockResolvedValue( getFindResultWithMultiHits({ data: [], total: Infinity }) ); - const response = await server.inject(getBulkActionRequest(), context); + const response = await server.inject( + getBulkActionRequest(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(400); expect(response.body).toEqual({ message: 'More than 10000 rules matched the filter query. Try to narrow it down.', @@ -105,7 +111,10 @@ describe.each([ }) ); - const response = await server.inject(getBulkActionEditRequest(), context); + const response = await server.inject( + getBulkActionEditRequest(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(500); expect(response.body).toEqual({ @@ -138,7 +147,10 @@ describe.each([ clients.rulesClient.disable.mockImplementation(async () => { throw new Error('Test error'); }); - const response = await server.inject(getBulkActionRequest(), context); + const response = await server.inject( + getBulkActionRequest(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(500); expect(response.body).toEqual({ message: 'Bulk edit failed', @@ -172,7 +184,10 @@ describe.each([ .fn() .mockResolvedValue({ valid: false, message: 'mocked validation message' }), }); - const response = await server.inject(getBulkActionRequest(), context); + const response = await server.inject( + getBulkActionRequest(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(500); expect(response.body).toEqual({ @@ -222,7 +237,7 @@ describe.each([ }, }); - const response = await server.inject(request, context); + const response = await server.inject(request, requestContextMock.convertContext(context)); expect(response.status).toEqual(500); expect(response.body).toEqual({ @@ -273,7 +288,7 @@ describe.each([ }, }); - const response = await server.inject(request, context); + const response = await server.inject(request, requestContextMock.convertContext(context)); expect(response.status).toEqual(500); expect(response.body).toEqual({ @@ -325,7 +340,10 @@ describe.each([ .mockImplementationOnce(() => ({ valid: true })) .mockImplementationOnce(() => ({ valid: true })), }); - const response = await server.inject(getBulkActionEditRequest(), context); + const response = await server.inject( + getBulkActionEditRequest(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(500); expect(response.body).toEqual({ @@ -372,7 +390,10 @@ describe.each([ clients.rulesClient.disable.mockImplementation(async () => { throw new Error('a'.repeat(1_300)); }); - const response = await server.inject(getBulkActionRequest(), context); + const response = await server.inject( + getBulkActionRequest(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(500); expect(response.body.attributes.errors[0].message.length).toEqual(1000); }); @@ -392,7 +413,7 @@ describe.each([ }, }); - const response = await server.inject(request, context); + const response = await server.inject(request, requestContextMock.convertContext(context)); expect(response.status).toEqual(500); expect(response.body).toEqual({ @@ -489,7 +510,7 @@ describe.each([ }, }); - const response = await server.inject(request, context); + const response = await server.inject(request, requestContextMock.convertContext(context)); expect(response.status).toEqual(400); expect(response.body.message).toEqual('More than 100 ids sent for bulk edit action.'); @@ -506,7 +527,7 @@ describe.each([ }, }); - const response = await server.inject(request, context); + const response = await server.inject(request, requestContextMock.convertContext(context)); expect(response.status).toEqual(400); expect(response.body.message).toEqual( @@ -524,7 +545,10 @@ describe.each([ }) ); - const response = await server.inject(getBulkActionEditRequest(), context); + const response = await server.inject( + getBulkActionEditRequest(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(200); expect(response.body).toEqual( diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/perform_bulk_action_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/perform_bulk_action_route.ts index e5b7dc2ef85e..ab3de5b8d0f3 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/perform_bulk_action_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/perform_bulk_action_route.ts @@ -146,13 +146,11 @@ const fetchRulesByQueryOrIds = async ({ query, ids, rulesClient, - isRuleRegistryEnabled, abortSignal, }: { query: string | undefined; ids: string[] | undefined; rulesClient: RulesClient; - isRuleRegistryEnabled: boolean; abortSignal: AbortSignal; }): Promise> => { if (ids) { @@ -160,7 +158,7 @@ const fetchRulesByQueryOrIds = async ({ concurrency: MAX_RULES_TO_UPDATE_IN_PARALLEL, items: ids, executor: async (id: string) => { - const rule = await readRules({ id, rulesClient, isRuleRegistryEnabled, ruleId: undefined }); + const rule = await readRules({ id, rulesClient, ruleId: undefined }); if (rule == null) { throw Error('Rule not found'); } @@ -171,7 +169,6 @@ const fetchRulesByQueryOrIds = async ({ } const { data, total } = await findRules({ - isRuleRegistryEnabled, rulesClient, perPage: MAX_RULES_TO_PROCESS_TOTAL, filter: query !== '' ? query : undefined, @@ -229,8 +226,7 @@ export const migrateRuleActions = async ({ export const performBulkActionRoute = ( router: SecuritySolutionPluginRouter, ml: SetupPlugins['ml'], - logger: Logger, - isRuleRegistryEnabled: boolean + logger: Logger ) => { router.post( { @@ -270,20 +266,27 @@ export const performBulkActionRoute = ( request.events.completed$.subscribe(() => abortController.abort()); try { - const rulesClient = context.alerting.getRulesClient(); - const ruleExecutionLog = context.securitySolution.getRuleExecutionLog(); - const exceptionsClient = context.lists?.getExceptionListClient(); - const savedObjectsClient = context.core.savedObjects.client; + const ctx = await context.resolve([ + 'core', + 'securitySolution', + 'alerting', + 'licensing', + 'lists', + ]); + + const rulesClient = ctx.alerting.getRulesClient(); + const ruleExecutionLog = ctx.securitySolution.getRuleExecutionLog(); + const exceptionsClient = ctx.lists?.getExceptionListClient(); + const savedObjectsClient = ctx.core.savedObjects.client; const mlAuthz = buildMlAuthz({ - license: context.licensing.license, + license: ctx.licensing.license, ml, request, savedObjectsClient, }); const fetchRulesOutcome = await fetchRulesByQueryOrIds({ - isRuleRegistryEnabled, rulesClient, query: body.query, ids: body.ids, @@ -378,7 +381,7 @@ export const performBulkActionRoute = ( throwAuthzError(await mlAuthz.validateRuleType(migratedRule.params.type)); const createdRule = await rulesClient.create({ - data: duplicateRule(migratedRule, isRuleRegistryEnabled), + data: duplicateRule(migratedRule), }); return createdRule; @@ -392,8 +395,7 @@ export const performBulkActionRoute = ( exceptionsClient, savedObjectsClient, rules.map(({ params }) => ({ rule_id: params.ruleId })), - logger, - isRuleRegistryEnabled + logger ); const responseBody = `${exported.rulesNdjson}${exported.exceptionLists}${exported.exportDetails}`; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/preview_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/preview_rules_route.ts index c9cc0cad0a65..00fc13315ff3 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/preview_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/preview_rules_route.ts @@ -80,14 +80,15 @@ export const previewRulesRoute = async ( async (context, request, response) => { const siemResponse = buildSiemResponse(response); const validationErrors = createRuleValidateTypeDependents(request.body); + const coreContext = await context.core; if (validationErrors.length) { return siemResponse.error({ statusCode: 400, body: validationErrors }); } try { const [, { data, security: securityService }] = await getStartServices(); const searchSourceClient = data.search.searchSource.asScoped(request); - const savedObjectsClient = context.core.savedObjects.client; - const siemClient = context.securitySolution.getAppClient(); + const savedObjectsClient = coreContext.savedObjects.client; + const siemClient = (await context.securitySolution).getAppClient(); let invocationCount = request.body.invocationCount; if ( @@ -103,17 +104,19 @@ export const previewRulesRoute = async ( }); } - const internalRule = convertCreateAPIToInternalSchema(request.body, siemClient, false); + const internalRule = convertCreateAPIToInternalSchema(request.body, siemClient); const previewRuleParams = internalRule.params; const mlAuthz = buildMlAuthz({ - license: context.licensing.license, + license: (await context.licensing).license, ml, request, savedObjectsClient, }); throwAuthzError(await mlAuthz.validateRuleType(internalRule.params.type)); - await context.lists?.getExceptionListClient().createEndpointList(); + + const listsContext = await context.lists; + await listsContext?.getExceptionListClient().createEndpointList(); const spaceId = siemClient.getSpaceId(); const previewId = uuid.v4(); @@ -234,13 +237,13 @@ export const previewRulesRoute = async ( shouldWriteAlerts, shouldStopExecution: () => false, alertFactory, - savedObjectsClient: context.core.savedObjects.client, + savedObjectsClient: coreContext.savedObjects.client, scopedClusterClient: wrapScopedClusterClient({ abortController, - scopedClusterClient: context.core.elasticsearch.client, + scopedClusterClient: coreContext.elasticsearch.client, }), searchSourceClient, - uiSettingsClient: context.core.uiSettings.client, + uiSettingsClient: coreContext.uiSettings.client, }, spaceId, startedAt: startedAt.toDate(), @@ -339,7 +342,7 @@ export const previewRulesRoute = async ( } // Refreshes alias to ensure index is able to be read before returning - await context.core.elasticsearch.client.asInternalUser.indices.refresh( + await coreContext.elasticsearch.client.asInternalUser.indices.refresh( { index: previewRuleDataClient.indexNameWithNamespace(spaceId), }, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/read_rules_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/read_rules_route.test.ts index c802f9857ce5..44c2c3a07550 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/read_rules_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/read_rules_route.test.ts @@ -17,15 +17,12 @@ import { nonRuleFindResult, getEmptySavedObjectsResponse, getRuleExecutionSummarySucceeded, - resolveAlertMock, + resolveRuleMock, } from '../__mocks__/request_responses'; import { requestMock, requestContextMock, serverMock } from '../__mocks__'; import { getQueryRuleParams } from '../../schemas/rule_schemas.mock'; -describe.each([ - ['Legacy', false], - ['RAC', true], -])('read_rules - %s', (_, isRuleRegistryEnabled) => { +describe('read_rules', () => { let server: ReturnType; let { clients, context } = requestContextMock.createTools(); let logger: ReturnType; @@ -36,61 +33,76 @@ describe.each([ logger = loggingSystemMock.createLogger(); ({ clients, context } = requestContextMock.createTools()); - clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled)); // rule exists + clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); // rule exists clients.savedObjectsClient.find.mockResolvedValue(getEmptySavedObjectsResponse()); // successful transform clients.ruleExecutionLog.getExecutionSummary.mockResolvedValue( getRuleExecutionSummarySucceeded() ); clients.rulesClient.resolve.mockResolvedValue({ - ...resolveAlertMock(isRuleRegistryEnabled, { + ...resolveRuleMock({ ...getQueryRuleParams(), }), id: myFakeId, }); - readRulesRoute(server.router, logger, isRuleRegistryEnabled); + readRulesRoute(server.router, logger); }); describe('status codes', () => { test('returns 200', async () => { - const response = await server.inject(getReadRequest(), context); + const response = await server.inject( + getReadRequest(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(200); }); test('returns 200 when reading a single rule outcome === exactMatch', async () => { - const response = await server.inject(getReadRequestWithId(myFakeId), context); + const response = await server.inject( + getReadRequestWithId(myFakeId), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(200); }); test('returns 200 when reading a single rule outcome === aliasMatch', async () => { clients.rulesClient.resolve.mockResolvedValue({ - ...resolveAlertMock(isRuleRegistryEnabled, { + ...resolveRuleMock({ ...getQueryRuleParams(), }), id: myFakeId, outcome: 'aliasMatch', }); - const response = await server.inject(getReadRequestWithId(myFakeId), context); + const response = await server.inject( + getReadRequestWithId(myFakeId), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(200); }); test('returns 200 when reading a single rule outcome === conflict', async () => { clients.rulesClient.resolve.mockResolvedValue({ - ...resolveAlertMock(isRuleRegistryEnabled, { + ...resolveRuleMock({ ...getQueryRuleParams(), }), id: myFakeId, outcome: 'conflict', alias_target_id: 'myaliastargetid', }); - const response = await server.inject(getReadRequestWithId(myFakeId), context); + const response = await server.inject( + getReadRequestWithId(myFakeId), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(200); expect(response.body.alias_target_id).toEqual('myaliastargetid'); }); test('returns error if requesting a non-rule', async () => { - clients.rulesClient.find.mockResolvedValue(nonRuleFindResult(isRuleRegistryEnabled)); - const response = await server.inject(getReadRequest(), context); + clients.rulesClient.find.mockResolvedValue(nonRuleFindResult()); + const response = await server.inject( + getReadRequest(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(404); expect(response.body).toEqual({ message: 'rule_id: "rule-1" not found', @@ -102,7 +114,10 @@ describe.each([ clients.rulesClient.find.mockImplementation(async () => { throw new Error('Test error'); }); - const response = await server.inject(getReadRequest(), context); + const response = await server.inject( + getReadRequest(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(500); expect(response.body).toEqual({ message: 'Test error', @@ -119,7 +134,7 @@ describe.each([ path: DETECTION_ENGINE_RULES_URL, query: { rule_id: 'DNE_RULE' }, }); - const response = await server.inject(request, context); + const response = await server.inject(request, requestContextMock.convertContext(context)); expect(response.status).toEqual(404); expect(response.body).toEqual({ message: 'rule_id: "DNE_RULE" not found', status_code: 404 }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/read_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/read_rules_route.ts index e1d24677ff4c..37af66b50c6b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/read_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/read_rules_route.ts @@ -22,11 +22,7 @@ import { readRules } from '../../rules/read_rules'; // eslint-disable-next-line no-restricted-imports import { legacyGetRuleActionsSavedObject } from '../../rule_actions/legacy_get_rule_actions_saved_object'; -export const readRulesRoute = ( - router: SecuritySolutionPluginRouter, - logger: Logger, - isRuleRegistryEnabled: boolean -) => { +export const readRulesRoute = (router: SecuritySolutionPluginRouter, logger: Logger) => { router.get( { path: DETECTION_ENGINE_RULES_URL, @@ -49,13 +45,12 @@ export const readRulesRoute = ( const { id, rule_id: ruleId } = request.query; try { - const rulesClient = context.alerting.getRulesClient(); - const ruleExecutionLog = context.securitySolution.getRuleExecutionLog(); - const savedObjectsClient = context.core.savedObjects.client; + const rulesClient = (await context.alerting).getRulesClient(); + const ruleExecutionLog = (await context.securitySolution).getRuleExecutionLog(); + const savedObjectsClient = (await context.core).savedObjects.client; const rule = await readRules({ id, - isRuleRegistryEnabled, rulesClient, ruleId, }); @@ -68,12 +63,7 @@ export const readRulesRoute = ( const ruleExecutionSummary = await ruleExecutionLog.getExecutionSummary(rule.id); - const transformed = transform( - rule, - ruleExecutionSummary, - isRuleRegistryEnabled, - legacyRuleActions - ); + const transformed = transform(rule, ruleExecutionSummary, legacyRuleActions); if (transformed == null) { return siemResponse.error({ statusCode: 500, body: 'Internal error transforming' }); } else { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_bulk_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_bulk_route.test.ts index 21ac33e29400..88720646fa6c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_bulk_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_bulk_route.test.ts @@ -10,7 +10,7 @@ import { mlServicesMock, mlAuthzMock as mockMlAuthzFactory } from '../../../mach import { buildMlAuthz } from '../../../machine_learning/authz'; import { getEmptyFindResult, - getAlertMock, + getRuleMock, getFindResultWithSingleHit, getUpdateBulkRequest, typicalMlRulePayload, @@ -24,10 +24,7 @@ import { loggingSystemMock } from '@kbn/core/server/mocks'; jest.mock('../../../machine_learning/authz', () => mockMlAuthzFactory.create()); -describe.each([ - ['Legacy', false], - ['RAC', true], -])('update_rules_bulk - %s', (_, isRuleRegistryEnabled) => { +describe('update_rules_bulk', () => { let server: ReturnType; let { clients, context } = requestContextMock.createTools(); let ml: ReturnType; @@ -38,19 +35,20 @@ describe.each([ ml = mlServicesMock.createSetupContract(); const logger = loggingSystemMock.createLogger(); - clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled)); - clients.rulesClient.update.mockResolvedValue( - getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()) - ); + clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); + clients.rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); clients.appClient.getSignalsIndex.mockReturnValue('.siem-signals-test-index'); - updateRulesBulkRoute(server.router, ml, isRuleRegistryEnabled, logger); + updateRulesBulkRoute(server.router, ml, logger); }); describe('status codes', () => { test('returns 200', async () => { - const response = await server.inject(getUpdateBulkRequest(), context); + const response = await server.inject( + getUpdateBulkRequest(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(200); }); @@ -62,7 +60,10 @@ describe.each([ rule_id: 'rule-1', }, ]; - const response = await server.inject(getUpdateBulkRequest(), context); + const response = await server.inject( + getUpdateBulkRequest(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(200); expect(response.body).toEqual(expected); @@ -79,7 +80,10 @@ describe.each([ rule_id: 'rule-1', }, ]; - const response = await server.inject(getUpdateBulkRequest(), context); + const response = await server.inject( + getUpdateBulkRequest(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(200); expect(response.body).toEqual(expected); }); @@ -96,7 +100,7 @@ describe.each([ body: [typicalMlRulePayload()], }); - const response = await server.inject(request, context); + const response = await server.inject(request, requestContextMock.convertContext(context)); expect(response.status).toEqual(200); expect(response.body).toEqual([ { @@ -117,7 +121,7 @@ describe.each([ path: DETECTION_ENGINE_RULES_BULK_UPDATE, body: [{ ...getCreateRulesSchemaMock(), rule_id: undefined }], }); - const response = await server.inject(noIdRequest, context); + const response = await server.inject(noIdRequest, requestContextMock.convertContext(context)); expect(response.body).toEqual([ { error: { message: 'either "id" or "rule_id" must be set', status_code: 400 }, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_bulk_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_bulk_route.ts index 0f9bc3535dc5..0ba03ea99954 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_bulk_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_bulk_route.ts @@ -30,7 +30,6 @@ import { getDeprecatedBulkEndpointHeader, logDeprecatedBulkEndpoint } from './ut export const updateRulesBulkRoute = ( router: SecuritySolutionPluginRouter, ml: SetupPlugins['ml'], - isRuleRegistryEnabled: boolean, logger: Logger ) => { router.put( @@ -48,13 +47,15 @@ export const updateRulesBulkRoute = ( const siemResponse = buildSiemResponse(response); - const rulesClient = context.alerting.getRulesClient(); - const ruleExecutionLog = context.securitySolution.getRuleExecutionLog(); - const savedObjectsClient = context.core.savedObjects.client; - const siemClient = context.securitySolution.getAppClient(); + const ctx = await context.resolve(['core', 'securitySolution', 'alerting', 'licensing']); + + const rulesClient = ctx.alerting.getRulesClient(); + const ruleExecutionLog = ctx.securitySolution.getRuleExecutionLog(); + const savedObjectsClient = ctx.core.savedObjects.client; + const siemClient = ctx.securitySolution.getAppClient(); const mlAuthz = buildMlAuthz({ - license: context.licensing.license, + license: ctx.licensing.license, ml, request, savedObjectsClient, @@ -76,7 +77,6 @@ export const updateRulesBulkRoute = ( throwAuthzError(await mlAuthz.validateRuleType(payloadRule.type)); const existingRule = await readRules({ - isRuleRegistryEnabled, rulesClient, ruleId: payloadRule.rule_id, id: payloadRule.id, @@ -96,12 +96,7 @@ export const updateRulesBulkRoute = ( }); if (rule != null) { const ruleExecutionSummary = await ruleExecutionLog.getExecutionSummary(rule.id); - return transformValidateBulkError( - rule.id, - rule, - ruleExecutionSummary, - isRuleRegistryEnabled - ); + return transformValidateBulkError(rule.id, rule, ruleExecutionSummary); } else { return getIdBulkError({ id: payloadRule.id, ruleId: payloadRule.rule_id }); } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_route.test.ts index 8a7a09a80c74..39040f4c9c4f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_route.test.ts @@ -9,7 +9,7 @@ import { mlServicesMock, mlAuthzMock as mockMlAuthzFactory } from '../../../mach import { buildMlAuthz } from '../../../machine_learning/authz'; import { getEmptyFindResult, - getAlertMock, + getRuleMock, getUpdateRequest, getFindResultWithSingleHit, getRuleExecutionSummarySucceeded, @@ -24,10 +24,7 @@ import { getQueryRuleParams } from '../../schemas/rule_schemas.mock'; jest.mock('../../../machine_learning/authz', () => mockMlAuthzFactory.create()); -describe.each([ - ['Legacy', false], - ['RAC', true], -])('update_rules - %s', (_, isRuleRegistryEnabled) => { +describe('update_rules', () => { let server: ReturnType; let { clients, context } = requestContextMock.createTools(); let ml: ReturnType; @@ -37,30 +34,32 @@ describe.each([ ({ clients, context } = requestContextMock.createTools()); ml = mlServicesMock.createSetupContract(); - clients.rulesClient.get.mockResolvedValue( - getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()) - ); // existing rule - clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled)); // rule exists - clients.rulesClient.update.mockResolvedValue( - getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()) - ); // successful update + clients.rulesClient.get.mockResolvedValue(getRuleMock(getQueryRuleParams())); // existing rule + clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); // rule exists + clients.rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); // successful update clients.ruleExecutionLog.getExecutionSummary.mockResolvedValue( getRuleExecutionSummarySucceeded() ); clients.appClient.getSignalsIndex.mockReturnValue('.siem-signals-test-index'); - updateRulesRoute(server.router, ml, isRuleRegistryEnabled); + updateRulesRoute(server.router, ml); }); describe('status codes', () => { test('returns 200', async () => { - const response = await server.inject(getUpdateRequest(), context); + const response = await server.inject( + getUpdateRequest(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(200); }); test('returns 404 when updating a single rule that does not exist', async () => { clients.rulesClient.find.mockResolvedValue(getEmptyFindResult()); - const response = await server.inject(getUpdateRequest(), context); + const response = await server.inject( + getUpdateRequest(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(404); expect(response.body).toEqual({ @@ -70,8 +69,11 @@ describe.each([ }); test('returns error when updating non-rule', async () => { - clients.rulesClient.find.mockResolvedValue(nonRuleFindResult(isRuleRegistryEnabled)); - const response = await server.inject(getUpdateRequest(), context); + clients.rulesClient.find.mockResolvedValue(nonRuleFindResult()); + const response = await server.inject( + getUpdateRequest(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(404); expect(response.body).toEqual({ @@ -84,7 +86,10 @@ describe.each([ clients.rulesClient.find.mockImplementation(async () => { throw new Error('Test error'); }); - const response = await server.inject(getUpdateRequest(), context); + const response = await server.inject( + getUpdateRequest(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(500); expect(response.body).toEqual({ message: 'Test error', @@ -104,7 +109,7 @@ describe.each([ body: typicalMlRulePayload(), }); - const response = await server.inject(request, context); + const response = await server.inject(request, requestContextMock.convertContext(context)); expect(response.status).toEqual(403); expect(response.body).toEqual({ message: 'mocked validation message', @@ -123,7 +128,7 @@ describe.each([ id: undefined, }, }); - const response = await server.inject(noIdRequest, context); + const response = await server.inject(noIdRequest, requestContextMock.convertContext(context)); expect(response.body).toEqual({ message: ['either "id" or "rule_id" must be set'], status_code: 400, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_route.ts index 8ac90748d921..ec16488944b5 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_route.ts @@ -22,11 +22,7 @@ import { buildRouteValidation } from '../../../../utils/build_validation/route_v import { legacyMigrate } from '../../rules/utils'; import { readRules } from '../../rules/read_rules'; -export const updateRulesRoute = ( - router: SecuritySolutionPluginRouter, - ml: SetupPlugins['ml'], - isRuleRegistryEnabled: boolean -) => { +export const updateRulesRoute = (router: SecuritySolutionPluginRouter, ml: SetupPlugins['ml']) => { router.put( { path: DETECTION_ENGINE_RULES_URL, @@ -44,12 +40,14 @@ export const updateRulesRoute = ( return siemResponse.error({ statusCode: 400, body: validationErrors }); } try { - const rulesClient = context.alerting.getRulesClient(); - const savedObjectsClient = context.core.savedObjects.client; - const siemClient = context.securitySolution.getAppClient(); + const ctx = await context.resolve(['core', 'securitySolution', 'alerting', 'licensing']); + + const rulesClient = ctx.alerting.getRulesClient(); + const savedObjectsClient = ctx.core.savedObjects.client; + const siemClient = ctx.securitySolution.getAppClient(); const mlAuthz = buildMlAuthz({ - license: context.licensing.license, + license: ctx.licensing.license, ml, request, savedObjectsClient, @@ -57,7 +55,6 @@ export const updateRulesRoute = ( throwAuthzError(await mlAuthz.validateRuleType(request.body.type)); const existingRule = await readRules({ - isRuleRegistryEnabled, rulesClient, ruleId: request.body.rule_id, id: request.body.id, @@ -76,13 +73,9 @@ export const updateRulesRoute = ( }); if (rule != null) { - const ruleExecutionLog = context.securitySolution.getRuleExecutionLog(); + const ruleExecutionLog = ctx.securitySolution.getRuleExecutionLog(); const ruleExecutionSummary = await ruleExecutionLog.getExecutionSummary(rule.id); - const [validated, errors] = transformValidate( - rule, - ruleExecutionSummary, - isRuleRegistryEnabled - ); + const [validated, errors] = transformValidate(rule, ruleExecutionSummary); if (errors != null) { return siemResponse.error({ statusCode: 500, body: errors }); } else { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils.test.ts index 93ec83bb750d..450741c5bf70 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils.test.ts @@ -22,7 +22,7 @@ import { swapActionIds, migrateLegacyActionsIds, } from './utils'; -import { getAlertMock } from '../__mocks__/request_responses'; +import { getRuleMock } from '../__mocks__/request_responses'; import { INTERNAL_IDENTIFIER } from '../../../../../common/constants'; import { PartialFilter } from '../../types'; import { BulkError, createBulkErrorObject } from '../utils'; @@ -64,21 +64,18 @@ const createMockImportRule = async (rule: ReturnType { +describe('utils', () => { const { clients } = requestContextMock.createTools(); describe('internalRuleToAPIResponse', () => { test('should work with a full data set', () => { - const fullRule = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const fullRule = getRuleMock(getQueryRuleParams()); const rule = internalRuleToAPIResponse(fullRule); expect(rule).toEqual(getOutputRuleAlertForRest()); }); test('should omit note if note is undefined', () => { - const fullRule = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const fullRule = getRuleMock(getQueryRuleParams()); fullRule.params.note = undefined; const rule = internalRuleToAPIResponse(fullRule); const { note, ...expectedWithoutNote } = getOutputRuleAlertForRest(); @@ -86,7 +83,7 @@ describe.each([ }); test('should return enabled is equal to false', () => { - const fullRule = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const fullRule = getRuleMock(getQueryRuleParams()); fullRule.enabled = false; const ruleWithEnabledFalse = internalRuleToAPIResponse(fullRule); const expected = getOutputRuleAlertForRest(); @@ -95,7 +92,7 @@ describe.each([ }); test('should return immutable is equal to false', () => { - const fullRule = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const fullRule = getRuleMock(getQueryRuleParams()); fullRule.params.immutable = false; const ruleWithEnabledFalse = internalRuleToAPIResponse(fullRule); const expected = getOutputRuleAlertForRest(); @@ -103,7 +100,7 @@ describe.each([ }); test('should work with tags but filter out any internal tags', () => { - const fullRule = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const fullRule = getRuleMock(getQueryRuleParams()); fullRule.tags = ['tag 1', 'tag 2', `${INTERNAL_IDENTIFIER}_some_other_value`]; const rule = internalRuleToAPIResponse(fullRule); const expected = getOutputRuleAlertForRest(); @@ -112,7 +109,7 @@ describe.each([ }); test('transforms ML Rule fields', () => { - const mlRule = getAlertMock(isRuleRegistryEnabled, getMlRuleParams()); + const mlRule = getRuleMock(getMlRuleParams()); mlRule.params.anomalyThreshold = 55; mlRule.params.machineLearningJobId = ['some_job_id']; mlRule.params.type = 'machine_learning'; @@ -128,7 +125,7 @@ describe.each([ }); test('transforms threat_matching fields', () => { - const threatRule = getAlertMock(isRuleRegistryEnabled, getThreatRuleParams()); + const threatRule = getRuleMock(getThreatRuleParams()); const threatFilters: PartialFilter[] = [ { query: { @@ -181,7 +178,7 @@ describe.each([ test('does not leak a lists structure in the transform which would cause validation issues', () => { const result: RuleAlertType & { lists: [] } = { lists: [], - ...getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()), + ...getRuleMock(getQueryRuleParams()), }; const rule = internalRuleToAPIResponse(result); expect(rule).toEqual( @@ -196,7 +193,7 @@ describe.each([ test('does not leak an exceptions_list structure in the transform which would cause validation issues', () => { const result: RuleAlertType & { exceptions_list: [] } = { exceptions_list: [], - ...getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()), + ...getRuleMock(getQueryRuleParams()), }; const rule = internalRuleToAPIResponse(result); expect(rule).toEqual( @@ -293,7 +290,7 @@ describe.each([ page: 1, perPage: 0, total: 0, - data: [getAlertMock(isRuleRegistryEnabled, getQueryRuleParams())], + data: [getRuleMock(getQueryRuleParams())], }, {}, {} @@ -313,7 +310,7 @@ describe.each([ page: 1, perPage: 0, total: 0, - data: [getAlertMock(isRuleRegistryEnabled, getQueryRuleParams())], + data: [getRuleMock(getQueryRuleParams())], }, {}, { @@ -340,7 +337,7 @@ describe.each([ ]; const legacyRuleActions: Record = { - [getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()).id]: { + [getRuleMock(getQueryRuleParams()).id]: { id: '123', actions, alertThrottle: '1h', @@ -352,7 +349,7 @@ describe.each([ page: 1, perPage: 0, total: 0, - data: [getAlertMock(isRuleRegistryEnabled, getQueryRuleParams())], + data: [getRuleMock(getQueryRuleParams())], }, {}, legacyRuleActions @@ -373,18 +370,14 @@ describe.each([ describe('transform', () => { test('outputs 200 if the data is of type siem alert', () => { - const output = transform( - getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()), - undefined, - isRuleRegistryEnabled - ); + const output = transform(getRuleMock(getQueryRuleParams()), undefined); const expected = getOutputRuleAlertForRest(); expect(output).toEqual(expected); }); test('returns 500 if the data is not of type siem alert', () => { const unsafeCast = { data: [{ random: 1 }] } as unknown as PartialRule; - const output = transform(unsafeCast, undefined, isRuleRegistryEnabled); + const output = transform(unsafeCast, undefined); expect(output).toBeNull(); }); }); @@ -496,15 +489,15 @@ describe.each([ }); test('given single alert will return the alert transformed', () => { - const result1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const result1 = getRuleMock(getQueryRuleParams()); const transformed = transformAlertsToRules([result1], {}); const expected = getOutputRuleAlertForRest(); expect(transformed).toEqual([expected]); }); test('given two alerts will return the two alerts transformed', () => { - const result1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); - const result2 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const result1 = getRuleMock(getQueryRuleParams()); + const result2 = getRuleMock(getQueryRuleParams()); result2.id = 'some other id'; result2.params.ruleId = 'some other id'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils.ts index 3c0ad839bc8b..27f3f160a528 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils.ts @@ -123,10 +123,9 @@ export const transformFindAlerts = ( export const transform = ( rule: PartialRule, ruleExecutionSummary?: RuleExecutionSummary | null, - isRuleRegistryEnabled?: boolean, legacyRuleActions?: LegacyRulesActionsSavedObject | null ): Partial | null => { - if (isAlertType(isRuleRegistryEnabled ?? false, rule)) { + if (isAlertType(rule)) { return internalRuleToAPIResponse(rule, ruleExecutionSummary, legacyRuleActions); } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils/import_rules_utils.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils/import_rules_utils.test.ts index 8d78592dc7fc..1a9d10d2a3d2 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils/import_rules_utils.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils/import_rules_utils.test.ts @@ -8,7 +8,7 @@ import { requestContextMock } from '../../__mocks__'; import { importRules } from './import_rules_utils'; import { - getAlertMock, + getRuleMock, getEmptyFindResult, getFindResultWithSingleHit, } from '../../__mocks__/request_responses'; @@ -30,7 +30,7 @@ describe('importRules', () => { beforeEach(() => { clients.rulesClient.find.mockResolvedValue(getEmptyFindResult()); - clients.rulesClient.update.mockResolvedValue(getAlertMock(true, getQueryRuleParams())); + clients.rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); clients.actionsClient.getAll.mockResolvedValue([]); jest.clearAllMocks(); @@ -42,12 +42,10 @@ describe('importRules', () => { rulesResponseAcc: [], mlAuthz, overwriteRules: false, - isRuleRegistryEnabled: true, savedObjectsClient: context.core.savedObjects.client, rulesClient: context.alerting.getRulesClient(), exceptionsClient: context.lists?.getExceptionListClient(), spaceId: 'default', - signalsIndex: '.signals-index', existingLists: {}, }); @@ -60,12 +58,10 @@ describe('importRules', () => { rulesResponseAcc: [], mlAuthz, overwriteRules: false, - isRuleRegistryEnabled: true, savedObjectsClient: context.core.savedObjects.client, rulesClient: context.alerting.getRulesClient(), exceptionsClient: context.lists?.getExceptionListClient(), spaceId: 'default', - signalsIndex: '.signals-index', existingLists: {}, }); @@ -93,12 +89,10 @@ describe('importRules', () => { rulesResponseAcc: [], mlAuthz, overwriteRules: false, - isRuleRegistryEnabled: true, savedObjectsClient: context.core.savedObjects.client, rulesClient: context.alerting.getRulesClient(), exceptionsClient: context.lists?.getExceptionListClient(), spaceId: 'default', - signalsIndex: '.signals-index', existingLists: {}, }); @@ -108,7 +102,7 @@ describe('importRules', () => { }); it('reports error if "overwriteRules" is "false" and matching rule found', async () => { - clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(true)); + clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); const result = await importRules({ ruleChunks: [ @@ -122,12 +116,10 @@ describe('importRules', () => { rulesResponseAcc: [], mlAuthz, overwriteRules: false, - isRuleRegistryEnabled: true, savedObjectsClient: context.core.savedObjects.client, rulesClient: context.alerting.getRulesClient(), exceptionsClient: context.lists?.getExceptionListClient(), spaceId: 'default', - signalsIndex: '.signals-index', existingLists: {}, }); @@ -142,7 +134,7 @@ describe('importRules', () => { }); it('patches rule if "overwriteRules" is "true" and matching rule found', async () => { - clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(true)); + clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); const result = await importRules({ ruleChunks: [ @@ -156,12 +148,10 @@ describe('importRules', () => { rulesResponseAcc: [], mlAuthz, overwriteRules: true, - isRuleRegistryEnabled: true, savedObjectsClient: context.core.savedObjects.client, rulesClient: context.alerting.getRulesClient(), exceptionsClient: context.lists?.getExceptionListClient(), spaceId: 'default', - signalsIndex: '.signals-index', existingLists: {}, }); @@ -185,12 +175,10 @@ describe('importRules', () => { rulesResponseAcc: [], mlAuthz, overwriteRules: true, - isRuleRegistryEnabled: true, savedObjectsClient: context.core.savedObjects.client, rulesClient: context.alerting.getRulesClient(), exceptionsClient: context.lists?.getExceptionListClient(), spaceId: 'default', - signalsIndex: '.signals-index', existingLists: {}, }); @@ -222,12 +210,10 @@ describe('importRules', () => { rulesResponseAcc: [], mlAuthz, overwriteRules: false, - isRuleRegistryEnabled: true, savedObjectsClient: context.core.savedObjects.client, rulesClient: context.alerting.getRulesClient(), exceptionsClient: context.lists?.getExceptionListClient(), spaceId: 'default', - signalsIndex: '.signals-index', existingLists: {}, }); @@ -244,7 +230,7 @@ describe('importRules', () => { it('reports error if "patchRules" throws', async () => { (patchRules as jest.Mock).mockRejectedValue(new Error('error patching rule')); - clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(true)); + clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); const result = await importRules({ ruleChunks: [ @@ -258,12 +244,10 @@ describe('importRules', () => { rulesResponseAcc: [], mlAuthz, overwriteRules: true, - isRuleRegistryEnabled: true, savedObjectsClient: context.core.savedObjects.client, rulesClient: context.alerting.getRulesClient(), exceptionsClient: context.lists?.getExceptionListClient(), spaceId: 'default', - signalsIndex: '.signals-index', existingLists: {}, }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils/import_rules_utils.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils/import_rules_utils.ts index 70083f8c0421..d603784fc708 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils/import_rules_utils.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils/import_rules_utils.ts @@ -41,13 +41,10 @@ export interface RuleExceptionsPromiseFromStreams { * @param mlAuthz {object} * @param overwriteRules {boolean} - whether to overwrite existing rules * with imported rules if their rule_id matches - * @param isRuleRegistryEnabled {boolean} - feature flag that should be - * removed as this is now on and no going back * @param rulesClient {object} * @param savedObjectsClient {object} * @param exceptionsClient {object} * @param spaceId {string} - space being used during import - * @param signalsIndex {string} - the signals index name * @param existingLists {object} - all exception lists referenced by * rules that were found to exist * @returns {Promise} an array of error and success messages from import @@ -57,24 +54,20 @@ export const importRules = async ({ rulesResponseAcc, mlAuthz, overwriteRules, - isRuleRegistryEnabled, rulesClient, savedObjectsClient, exceptionsClient, spaceId, - signalsIndex, existingLists, }: { ruleChunks: PromiseFromStreams[][]; rulesResponseAcc: ImportRuleResponse[]; mlAuthz: MlAuthz; overwriteRules: boolean; - isRuleRegistryEnabled: boolean; rulesClient: RulesClient; savedObjectsClient: SavedObjectsClientContract; exceptionsClient: ExceptionListClient | undefined; spaceId: string; - signalsIndex: string; existingLists: Record; }) => { let importRuleResponse: ImportRuleResponse[] = [...rulesResponseAcc]; @@ -167,7 +160,6 @@ export const importRules = async ({ const filters: PartialFilter[] | undefined = filtersRest as PartialFilter[]; throwAuthzError(await mlAuthz.validateRuleType(type)); const rule = await readRules({ - isRuleRegistryEnabled, rulesClient, ruleId, id: undefined, @@ -175,7 +167,6 @@ export const importRules = async ({ if (rule == null) { await createRules({ - isRuleRegistryEnabled, rulesClient, anomalyThreshold, author, @@ -190,7 +181,7 @@ export const importRules = async ({ language, license, machineLearningJobId, - outputIndex: signalsIndex, + outputIndex: '', savedId, timelineId, timelineTitle, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/validate.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/validate.test.ts index f2b2c2b5d1f2..0b8c49cdb4d1 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/validate.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/validate.test.ts @@ -8,7 +8,7 @@ import { transformValidate, transformValidateBulkError } from './validate'; import { BulkError } from '../utils'; import { RulesSchema } from '../../../../../common/detection_engine/schemas/response'; -import { getAlertMock, getRuleExecutionSummarySucceeded } from '../__mocks__/request_responses'; +import { getRuleMock, getRuleExecutionSummarySucceeded } from '../__mocks__/request_responses'; import { getListArrayMock } from '../../../../../common/detection_engine/schemas/types/lists.mock'; import { getThreatMock } from '../../../../../common/detection_engine/schemas/types/threat.mock'; import { getQueryRuleParams } from '../../schemas/rule_schemas.mock'; @@ -65,23 +65,20 @@ export const ruleOutput = (): RulesSchema => ({ timeline_id: 'some-timeline-id', }); -describe.each([ - ['Legacy', false], - ['RAC', true], -])('validate - %s', (_, isRuleRegistryEnabled) => { +describe('validate', () => { describe('transformValidate', () => { test('it should do a validation correctly of a partial alert', () => { - const ruleAlert = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); - const [validated, errors] = transformValidate(ruleAlert, null, isRuleRegistryEnabled); + const ruleAlert = getRuleMock(getQueryRuleParams()); + const [validated, errors] = transformValidate(ruleAlert, null); expect(validated).toEqual(ruleOutput()); expect(errors).toEqual(null); }); test('it should do an in-validation correctly of a partial alert', () => { - const ruleAlert = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const ruleAlert = getRuleMock(getQueryRuleParams()); // @ts-expect-error delete ruleAlert.name; - const [validated, errors] = transformValidate(ruleAlert, null, isRuleRegistryEnabled); + const [validated, errors] = transformValidate(ruleAlert, null); expect(validated).toEqual(null); expect(errors).toEqual('Invalid value "undefined" supplied to "name"'); }); @@ -89,26 +86,16 @@ describe.each([ describe('transformValidateBulkError', () => { test('it should do a validation correctly of a rule id', () => { - const ruleAlert = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); - const validatedOrError = transformValidateBulkError( - 'rule-1', - ruleAlert, - null, - isRuleRegistryEnabled - ); + const ruleAlert = getRuleMock(getQueryRuleParams()); + const validatedOrError = transformValidateBulkError('rule-1', ruleAlert, null); expect(validatedOrError).toEqual(ruleOutput()); }); test('it should do an in-validation correctly of a rule id', () => { - const ruleAlert = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const ruleAlert = getRuleMock(getQueryRuleParams()); // @ts-expect-error delete ruleAlert.name; - const validatedOrError = transformValidateBulkError( - 'rule-1', - ruleAlert, - null, - isRuleRegistryEnabled - ); + const validatedOrError = transformValidateBulkError('rule-1', ruleAlert, null); const expected: BulkError = { error: { message: 'Invalid value "undefined" supplied to "name"', @@ -120,14 +107,9 @@ describe.each([ }); test('it should do a validation correctly of a rule id with rule execution summary passed in', () => { - const rule = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const rule = getRuleMock(getQueryRuleParams()); const ruleExecutionSumary = getRuleExecutionSummarySucceeded(); - const validatedOrError = transformValidateBulkError( - 'rule-1', - rule, - ruleExecutionSumary, - isRuleRegistryEnabled - ); + const validatedOrError = transformValidateBulkError('rule-1', rule, ruleExecutionSumary); const expected: RulesSchema = { ...ruleOutput(), execution_summary: ruleExecutionSumary, @@ -136,15 +118,10 @@ describe.each([ }); test('it should return error object if "alert" is not expected alert type', () => { - const ruleAlert = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const ruleAlert = getRuleMock(getQueryRuleParams()); // @ts-expect-error delete ruleAlert.alertTypeId; - const validatedOrError = transformValidateBulkError( - 'rule-1', - ruleAlert, - null, - isRuleRegistryEnabled - ); + const validatedOrError = transformValidateBulkError('rule-1', ruleAlert, null); const expected: BulkError = { error: { message: 'Internal error transforming', diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/validate.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/validate.ts index bde5d3ebf202..852c06ba394f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/validate.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/validate.ts @@ -28,15 +28,9 @@ import { internalRuleToAPIResponse } from '../../schemas/rule_converters'; export const transformValidate = ( rule: PartialRule, ruleExecutionSummary: RuleExecutionSummary | null, - isRuleRegistryEnabled?: boolean, legacyRuleActions?: LegacyRulesActionsSavedObject | null ): [RulesSchema | null, string | null] => { - const transformed = transform( - rule, - ruleExecutionSummary, - isRuleRegistryEnabled, - legacyRuleActions - ); + const transformed = transform(rule, ruleExecutionSummary, legacyRuleActions); if (transformed == null) { return [null, 'Internal error transforming']; } else { @@ -47,15 +41,9 @@ export const transformValidate = ( export const newTransformValidate = ( rule: PartialRule, ruleExecutionSummary: RuleExecutionSummary | null, - isRuleRegistryEnabled?: boolean, legacyRuleActions?: LegacyRulesActionsSavedObject | null ): [FullResponseSchema | null, string | null] => { - const transformed = transform( - rule, - ruleExecutionSummary, - isRuleRegistryEnabled, - legacyRuleActions - ); + const transformed = transform(rule, ruleExecutionSummary, legacyRuleActions); if (transformed == null) { return [null, 'Internal error transforming']; } else { @@ -66,10 +54,9 @@ export const newTransformValidate = ( export const transformValidateBulkError = ( ruleId: string, rule: PartialRule, - ruleExecutionSummary: RuleExecutionSummary | null, - isRuleRegistryEnabled?: boolean + ruleExecutionSummary: RuleExecutionSummary | null ): RulesSchema | BulkError => { - if (isAlertType(isRuleRegistryEnabled ?? false, rule)) { + if (isAlertType(rule)) { const transformed = internalRuleToAPIResponse(rule, ruleExecutionSummary); const [validated, errors] = validateNonExact(transformed, rulesSchema); if (errors != null || validated == null) { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/create_signals_migration_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/create_signals_migration_route.ts index 8da147d64a6c..df4ff25e9c64 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/create_signals_migration_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/create_signals_migration_route.ts @@ -39,9 +39,12 @@ export const createSignalsMigrationRoute = ( const { index: indices, ...reindexOptions } = request.body; try { - const esClient = context.core.elasticsearch.client.asCurrentUser; - const soClient = context.core.savedObjects.client; - const appClient = context.securitySolution?.getAppClient(); + const core = await context.core; + const securitySolution = await context.securitySolution; + + const esClient = core.elasticsearch.client.asCurrentUser; + const soClient = core.savedObjects.client; + const appClient = securitySolution?.getAppClient(); if (!appClient) { return siemResponse.error({ statusCode: 404 }); } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/delete_signals_migration_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/delete_signals_migration_route.ts index 65ed42a0a166..f94aff365f49 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/delete_signals_migration_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/delete_signals_migration_route.ts @@ -35,9 +35,12 @@ export const deleteSignalsMigrationRoute = ( const { migration_ids: migrationIds } = request.body; try { - const esClient = context.core.elasticsearch.client.asCurrentUser; - const soClient = context.core.savedObjects.client; - const appClient = context.securitySolution?.getAppClient(); + const core = await context.core; + const securitySolution = await context.securitySolution; + + const esClient = core.elasticsearch.client.asCurrentUser; + const soClient = core.savedObjects.client; + const appClient = securitySolution?.getAppClient(); if (!appClient) { return siemResponse.error({ statusCode: 404 }); } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/finalize_signals_migration_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/finalize_signals_migration_route.ts index a6f5f28fce8e..6029ad8e86bb 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/finalize_signals_migration_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/finalize_signals_migration_route.ts @@ -35,12 +35,16 @@ export const finalizeSignalsMigrationRoute = ( }, async (context, request, response) => { const siemResponse = buildSiemResponse(response); - const esClient = context.core.elasticsearch.client.asCurrentUser; - const soClient = context.core.savedObjects.client; + + const core = await context.core; + const securitySolution = await context.securitySolution; + + const esClient = core.elasticsearch.client.asCurrentUser; + const soClient = core.savedObjects.client; const { migration_ids: migrationIds } = request.body; try { - const appClient = context.securitySolution?.getAppClient(); + const appClient = securitySolution?.getAppClient(); if (!appClient) { return siemResponse.error({ statusCode: 404 }); } @@ -55,7 +59,7 @@ export const finalizeSignalsMigrationRoute = ( soClient, }); - const spaceId = context.securitySolution.getSpaceId(); + const spaceId = securitySolution.getSpaceId(); const signalsAlias = ruleDataService.getResourceName(`security.alerts-${spaceId}`); const finalizeResults = await Promise.all( migrations.map(async (migration) => { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/get_signals_migration_status_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/get_signals_migration_status_route.ts index d800cead20cd..f23bf4ae4185 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/get_signals_migration_status_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/get_signals_migration_status_route.ts @@ -31,11 +31,15 @@ export const getSignalsMigrationStatusRoute = (router: SecuritySolutionPluginRou }, async (context, request, response) => { const siemResponse = buildSiemResponse(response); - const esClient = context.core.elasticsearch.client.asCurrentUser; - const soClient = context.core.savedObjects.client; + + const core = await context.core; + const securitySolution = await context.securitySolution; + + const esClient = core.elasticsearch.client.asCurrentUser; + const soClient = core.savedObjects.client; try { - const appClient = context.securitySolution?.getAppClient(); + const appClient = securitySolution?.getAppClient(); if (!appClient) { return siemResponse.error({ statusCode: 404 }); } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/open_close_signals.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/open_close_signals.test.ts index 9cc93bc7f590..5a46d7a9b55f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/open_close_signals.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/open_close_signals.test.ts @@ -44,12 +44,18 @@ describe('set signal status', () => { describe('status on signal', () => { test('returns 200 when setting a status on a signal by ids', async () => { - const response = await server.inject(getSetSignalStatusByIdsRequest(), context); + const response = await server.inject( + getSetSignalStatusByIdsRequest(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(200); }); test('returns 200 when setting a status on a signal by query', async () => { - const response = await server.inject(getSetSignalStatusByQueryRequest(), context); + const response = await server.inject( + getSetSignalStatusByQueryRequest(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(200); }); @@ -68,7 +74,10 @@ describe('set signal status', () => { context.core.elasticsearch.client.asCurrentUser.updateByQuery.mockRejectedValue( new Error('Test error') ); - const response = await server.inject(getSetSignalStatusByQueryRequest(), context); + const response = await server.inject( + getSetSignalStatusByQueryRequest(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(500); expect(response.body).toEqual({ message: 'Test error', @@ -106,7 +115,7 @@ describe('set signal status', () => { path: DETECTION_ENGINE_SIGNALS_STATUS_URL, body: setStatusSignalMissingIdsAndQueryPayload(), }); - const response = await server.inject(request, context); + const response = await server.inject(request, requestContextMock.convertContext(context)); expect(response.status).toEqual(400); expect(response.body).toEqual({ message: ['either "signal_ids" or "query" must be set'], diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/open_close_signals_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/open_close_signals_route.ts index 8482dd3b94e1..63b90ccc4cfc 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/open_close_signals_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/open_close_signals_route.ts @@ -49,11 +49,13 @@ export const setSignalsStatusRoute = ( }, async (context, request, response) => { const { conflicts, signal_ids: signalIds, query, status } = request.body; - const esClient = context.core.elasticsearch.client.asCurrentUser; - const siemClient = context.securitySolution?.getAppClient(); + const core = await context.core; + const securitySolution = await context.securitySolution; + const esClient = core.elasticsearch.client.asCurrentUser; + const siemClient = securitySolution?.getAppClient(); const siemResponse = buildSiemResponse(response); const validationErrors = setSignalStatusValidateTypeDependents(request.body); - const spaceId = context.securitySolution?.getSpaceId() ?? 'default'; + const spaceId = securitySolution?.getSpaceId() ?? 'default'; if (validationErrors.length) { return siemResponse.error({ statusCode: 400, body: validationErrors }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/query_signals_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/query_signals_route.test.ts index b3de63e6d278..8a8af78a9b86 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/query_signals_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/query_signals_route.test.ts @@ -35,7 +35,10 @@ describe('query for signal', () => { describe('query and agg on signals index', () => { test('returns 200 when using single query', async () => { - const response = await server.inject(getSignalsQueryRequest(), context); + const response = await server.inject( + getSignalsQueryRequest(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(200); expect(ruleDataClient.getReader().search).toHaveBeenCalledWith( @@ -46,7 +49,10 @@ describe('query for signal', () => { }); test('returns 200 when using single agg', async () => { - const response = await server.inject(getSignalsAggsQueryRequest(), context); + const response = await server.inject( + getSignalsAggsQueryRequest(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(200); expect(ruleDataClient.getReader().search).toHaveBeenCalledWith( @@ -55,7 +61,10 @@ describe('query for signal', () => { }); test('returns 200 when using aggs and query together', async () => { - const response = await server.inject(getSignalsAggsAndQueryRequest(), context); + const response = await server.inject( + getSignalsAggsAndQueryRequest(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(200); expect(ruleDataClient.getReader().search).toHaveBeenCalledWith( @@ -70,7 +79,10 @@ describe('query for signal', () => { test('catches error if query throws error', async () => { ruleDataClient.getReader().search.mockRejectedValue(new Error('Test error')); - const response = await server.inject(getSignalsAggsQueryRequest(), context); + const response = await server.inject( + getSignalsAggsQueryRequest(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(500); expect(response.body).toEqual({ message: 'Test error', @@ -120,7 +132,7 @@ describe('query for signal', () => { path: DETECTION_ENGINE_QUERY_SIGNALS_URL, body: {}, }); - const response = await server.inject(request, context); + const response = await server.inject(request, requestContextMock.convertContext(context)); expect(response.status).toEqual(400); expect(response.body).toEqual({ message: '"value" must have at least 1 children', diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/query_signals_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/query_signals_route.ts index 3740d187347b..7b8216084ac9 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/query_signals_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/query_signals_route.ts @@ -52,20 +52,19 @@ export const querySignalsRoute = ( } try { - const result = await ruleDataClient - ?.getReader({ namespace: context.securitySolution.getSpaceId() }) - .search({ - body: { - query, - // Note: I use a spread operator to please TypeScript with aggs: { ...aggs } - aggs: { ...aggs }, - _source, - track_total_hits, - size, - runtime_mappings: runtime_mappings as MappingRuntimeFields, - }, - ignore_unavailable: true, - }); + const spaceId = (await context.securitySolution).getSpaceId(); + const result = await ruleDataClient?.getReader({ namespace: spaceId }).search({ + body: { + query, + // Note: I use a spread operator to please TypeScript with aggs: { ...aggs } + aggs: { ...aggs }, + _source, + track_total_hits, + size, + runtime_mappings: runtime_mappings as MappingRuntimeFields, + }, + ignore_unavailable: true, + }); return response.ok({ body: result }); } catch (err) { // error while getting or updating signal with id: id in signal index .siem-signals diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/tags/read_tags_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/tags/read_tags_route.ts index 04464e5d6f5a..508495cdeb9e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/tags/read_tags_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/tags/read_tags_route.ts @@ -12,10 +12,7 @@ import { buildSiemResponse } from '../utils'; import { readTags } from '../../tags/read_tags'; -export const readTagsRoute = ( - router: SecuritySolutionPluginRouter, - isRuleRegistryEnabled: boolean -) => { +export const readTagsRoute = (router: SecuritySolutionPluginRouter) => { router.get( { path: DETECTION_ENGINE_TAGS_URL, @@ -26,7 +23,7 @@ export const readTagsRoute = ( }, async (context, request, response) => { const siemResponse = buildSiemResponse(response); - const rulesClient = context.alerting?.getRulesClient(); + const rulesClient = (await context.alerting)?.getRulesClient(); if (!rulesClient) { return siemResponse.error({ statusCode: 404 }); @@ -34,7 +31,6 @@ export const readTagsRoute = ( try { const tags = await readTags({ - isRuleRegistryEnabled, rulesClient, }); return response.ok({ body: tags }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/utils.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/utils.test.ts index 4e614b5fc838..d9845f85b293 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/utils.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/utils.test.ts @@ -10,10 +10,7 @@ import { transformBulkError, BulkError, convertToSnakeCase, SiemResponseFactory import { responseMock } from './__mocks__'; import { CustomHttpRequestError } from '../../../utils/custom_http_request_error'; -describe.each([ - ['Legacy', false], - ['RAC', true], -])('utils - %s', (_, isRuleRegistryEnabled) => { +describe('utils', () => { describe('transformBulkError', () => { test('returns transformed object if it is a custom error object', () => { const customError = new CustomHttpRequestError('some custom error message', 400); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/create_rules.mock.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/create_rules.mock.ts index 126726ab08a7..1a41adb4f6da 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/create_rules.mock.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/create_rules.mock.ts @@ -8,8 +8,7 @@ import { CreateRulesOptions } from './types'; import { rulesClientMock } from '@kbn/alerting-plugin/server/mocks'; -export const getCreateRulesOptionsMock = (isRuleRegistryEnabled: boolean): CreateRulesOptions => ({ - isRuleRegistryEnabled, +export const getCreateRulesOptionsMock = (): CreateRulesOptions => ({ author: ['Elastic'], buildingBlockType: undefined, rulesClient: rulesClientMock.create(), @@ -62,10 +61,7 @@ export const getCreateRulesOptionsMock = (isRuleRegistryEnabled: boolean): Creat actions: [], }); -export const getCreateMlRulesOptionsMock = ( - isRuleRegistryEnabled: boolean -): CreateRulesOptions => ({ - isRuleRegistryEnabled, +export const getCreateMlRulesOptionsMock = (): CreateRulesOptions => ({ author: ['Elastic'], buildingBlockType: undefined, rulesClient: rulesClientMock.create(), @@ -118,9 +114,7 @@ export const getCreateMlRulesOptionsMock = ( actions: [], }); -export const getCreateThreatMatchRulesOptionsMock = ( - isRuleRegistryEnabled: boolean -): CreateRulesOptions => ({ +export const getCreateThreatMatchRulesOptionsMock = (): CreateRulesOptions => ({ actions: [], anomalyThreshold: undefined, author: ['Elastic'], @@ -136,7 +130,6 @@ export const getCreateThreatMatchRulesOptionsMock = ( immutable: false, index: ['*'], interval: '5m', - isRuleRegistryEnabled, itemsPerSearch: undefined, language: 'kuery', license: 'Elastic License', diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/create_rules.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/create_rules.test.ts index 3d5619ab1306..ab683effd4c1 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/create_rules.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/create_rules.test.ts @@ -12,12 +12,9 @@ import { } from './create_rules.mock'; import { DEFAULT_INDICATOR_SOURCE_PATH } from '../../../../common/constants'; -describe.each([ - ['Legacy', false], - ['RAC', true], -])('createRules - %s', (_, isRuleRegistryEnabled) => { +describe('createRules', () => { it('calls the rulesClient with legacy ML params', async () => { - const ruleOptions = getCreateMlRulesOptionsMock(isRuleRegistryEnabled); + const ruleOptions = getCreateMlRulesOptionsMock(); await createRules(ruleOptions); expect(ruleOptions.rulesClient.create).toHaveBeenCalledWith( expect.objectContaining({ @@ -33,7 +30,7 @@ describe.each([ it('calls the rulesClient with ML params', async () => { const ruleOptions = { - ...getCreateMlRulesOptionsMock(isRuleRegistryEnabled), + ...getCreateMlRulesOptionsMock(), machineLearningJobId: ['new_job_1', 'new_job_2'], }; await createRules(ruleOptions); @@ -50,7 +47,7 @@ describe.each([ }); it('populates a threatIndicatorPath value for threat_match rule if empty', async () => { - const ruleOptions = getCreateThreatMatchRulesOptionsMock(isRuleRegistryEnabled); + const ruleOptions = getCreateThreatMatchRulesOptionsMock(); delete ruleOptions.threatIndicatorPath; await createRules(ruleOptions); expect(ruleOptions.rulesClient.create).toHaveBeenCalledWith( @@ -65,7 +62,7 @@ describe.each([ }); it('does not populate a threatIndicatorPath value for other rules if empty', async () => { - const ruleOptions = getCreateMlRulesOptionsMock(isRuleRegistryEnabled); + const ruleOptions = getCreateMlRulesOptionsMock(); delete ruleOptions.threatIndicatorPath; await createRules(ruleOptions); expect(ruleOptions.rulesClient.create).not.toHaveBeenCalledWith( diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/create_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/create_rules.ts index 9ba39746ae4d..ed2d1e823cd1 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/create_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/create_rules.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { SIGNALS_ID, ruleTypeMappings } from '@kbn/securitysolution-rules'; +import { ruleTypeMappings } from '@kbn/securitysolution-rules'; import { RuleTypeParams, SanitizedRule } from '@kbn/alerting-plugin/common'; import { @@ -75,7 +75,6 @@ export const createRules = async ({ version, exceptionsList, actions, - isRuleRegistryEnabled, id, }: CreateRulesOptions): Promise> => { const rule = await rulesClient.create({ @@ -85,7 +84,7 @@ export const createRules = async ({ data: { name, tags: addTags(tags, ruleId, immutable), - alertTypeId: isRuleRegistryEnabled ? ruleTypeMappings[type] : SIGNALS_ID, + alertTypeId: ruleTypeMappings[type], consumer: SERVER_APP_ID, params: { anomalyThreshold, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/duplicate_rule.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/duplicate_rule.test.ts index 6d4da61efcc8..f93312ade4cf 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/duplicate_rule.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/duplicate_rule.test.ts @@ -18,73 +18,70 @@ describe('duplicateRule', () => { (uuid.v4 as jest.Mock).mockReturnValue('newId'); expect( - duplicateRule( - { - id: 'oldTestRuleId', - notifyWhen: 'onActiveAlert', - name: 'test', - tags: ['test', '__internal_rule_id:oldTestRuleId', `${INTERNAL_IMMUTABLE_KEY}:false`], - alertTypeId: 'siem.signals', - consumer: 'siem', - params: { - savedId: undefined, - author: [], - description: 'test', - ruleId: 'oldTestRuleId', - falsePositives: [], - from: 'now-360s', - immutable: false, - license: '', - outputIndex: '.siem-signals-default', - meta: undefined, - maxSignals: 100, - riskScore: 42, - riskScoreMapping: [], - severity: 'low', - severityMapping: [], - threat: [], - to: 'now', - references: [], - version: 1, - exceptionsList: [], - type: 'query', - language: 'kuery', - index: [], - query: 'process.args : "chmod"', - filters: [], - buildingBlockType: undefined, - namespace: undefined, - note: undefined, - timelineId: undefined, - timelineTitle: undefined, - ruleNameOverride: undefined, - timestampOverride: undefined, - }, - schedule: { - interval: '5m', - }, - enabled: false, - actions: [], - throttle: null, - apiKeyOwner: 'kibana', - createdBy: 'kibana', - updatedBy: 'kibana', - muteAll: false, - mutedInstanceIds: [], - updatedAt: new Date(2021, 0), - createdAt: new Date(2021, 0), - scheduledTaskId: undefined, - executionStatus: { - lastExecutionDate: new Date(2021, 0), - status: 'ok', - }, + duplicateRule({ + id: 'oldTestRuleId', + notifyWhen: 'onActiveAlert', + name: 'test', + tags: ['test', '__internal_rule_id:oldTestRuleId', `${INTERNAL_IMMUTABLE_KEY}:false`], + alertTypeId: 'siem.signals', + consumer: 'siem', + params: { + savedId: undefined, + author: [], + description: 'test', + ruleId: 'oldTestRuleId', + falsePositives: [], + from: 'now-360s', + immutable: false, + license: '', + outputIndex: '.siem-signals-default', + meta: undefined, + maxSignals: 100, + riskScore: 42, + riskScoreMapping: [], + severity: 'low', + severityMapping: [], + threat: [], + to: 'now', + references: [], + version: 1, + exceptionsList: [], + type: 'query', + language: 'kuery', + index: [], + query: 'process.args : "chmod"', + filters: [], + buildingBlockType: undefined, + namespace: undefined, + note: undefined, + timelineId: undefined, + timelineTitle: undefined, + ruleNameOverride: undefined, + timestampOverride: undefined, }, - false - ) + schedule: { + interval: '5m', + }, + enabled: false, + actions: [], + throttle: null, + apiKeyOwner: 'kibana', + createdBy: 'kibana', + updatedBy: 'kibana', + muteAll: false, + mutedInstanceIds: [], + updatedAt: new Date(2021, 0), + createdAt: new Date(2021, 0), + scheduledTaskId: undefined, + executionStatus: { + lastExecutionDate: new Date(2021, 0), + status: 'ok', + }, + }) ).toMatchInlineSnapshot(` Object { "actions": Array [], - "alertTypeId": "siem.signals", + "alertTypeId": "siem.queryRule", "consumer": "siem", "enabled": false, "name": "test [Duplicate]", diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/duplicate_rule.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/duplicate_rule.ts index 4efd6934aeee..d3dba0f61df2 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/duplicate_rule.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/duplicate_rule.ts @@ -8,7 +8,7 @@ import uuid from 'uuid'; import { i18n } from '@kbn/i18n'; -import { ruleTypeMappings, SIGNALS_ID } from '@kbn/securitysolution-rules'; +import { ruleTypeMappings } from '@kbn/securitysolution-rules'; import { SanitizedRule } from '@kbn/alerting-plugin/common'; import { SERVER_APP_ID } from '../../../../common/constants'; @@ -22,15 +22,12 @@ const DUPLICATE_TITLE = i18n.translate( } ); -export const duplicateRule = ( - rule: SanitizedRule, - isRuleRegistryEnabled: boolean -): InternalRuleCreate => { +export const duplicateRule = (rule: SanitizedRule): InternalRuleCreate => { const newRuleId = uuid.v4(); return { name: `${rule.name} [${DUPLICATE_TITLE}]`, tags: addTags(rule.tags, newRuleId, false), - alertTypeId: isRuleRegistryEnabled ? ruleTypeMappings[rule.params.type] : SIGNALS_ID, + alertTypeId: ruleTypeMappings[rule.params.type], consumer: SERVER_APP_ID, params: { ...rule.params, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/find_rules.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/find_rules.test.ts index f4270b359c4d..77fed17c4026 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/find_rules.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/find_rules.test.ts @@ -12,7 +12,6 @@ import { QUERY_RULE_TYPE_ID, SAVED_QUERY_RULE_TYPE_ID, THRESHOLD_RULE_TYPE_ID, - SIGNALS_ID, } from '@kbn/securitysolution-rules'; import { getFilter } from './find_rules'; @@ -25,26 +24,13 @@ const allAlertTypeIds = `(alert.attributes.alertTypeId: ${EQL_RULE_TYPE_ID} OR alert.attributes.alertTypeId: ${THRESHOLD_RULE_TYPE_ID})`.replace(/[\n\r]/g, ''); describe('find_rules', () => { - const fullFilterTestCases: Array<[boolean, string]> = [ - [false, `alert.attributes.alertTypeId: ${SIGNALS_ID} AND alert.attributes.enabled: true`], - [true, `${allAlertTypeIds} AND alert.attributes.enabled: true`], - ]; - const nullFilterTestCases: Array<[boolean, string]> = [ - [false, `alert.attributes.alertTypeId: ${SIGNALS_ID}`], - [true, allAlertTypeIds], - ]; + test('it returns a full filter with an AND if sent down', () => { + expect(getFilter('alert.attributes.enabled: true')).toEqual( + `${allAlertTypeIds} AND alert.attributes.enabled: true` + ); + }); - test.each(fullFilterTestCases)( - 'it returns a full filter with an AND if sent down [rule registry enabled: %p]', - (isRuleRegistryEnabled, expected) => { - expect(getFilter('alert.attributes.enabled: true', isRuleRegistryEnabled)).toEqual(expected); - } - ); - - test.each(nullFilterTestCases)( - 'it returns existing filter with no AND when not set [rule registry enabled: %p]', - (isRuleRegistryEnabled, expected) => { - expect(getFilter(null, isRuleRegistryEnabled)).toEqual(expected); - } - ); + test('it returns existing filter with no AND when not set [rule registry enabled: %p]', () => { + expect(getFilter(null)).toEqual(allAlertTypeIds); + }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/find_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/find_rules.ts index 4f23d1425344..3d95a13c428a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/find_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/find_rules.ts @@ -5,22 +5,17 @@ * 2.0. */ -import { SIGNALS_ID, ruleTypeMappings } from '@kbn/securitysolution-rules'; +import { ruleTypeMappings } from '@kbn/securitysolution-rules'; import { FindResult } from '@kbn/alerting-plugin/server'; import { RuleParams } from '../schemas/rule_schemas'; import { FindRuleOptions } from './types'; -export const getFilter = ( - filter: string | null | undefined, - isRuleRegistryEnabled: boolean = false -) => { - const alertTypeFilter = isRuleRegistryEnabled - ? `(${Object.values(ruleTypeMappings) - .map((type) => `alert.attributes.alertTypeId: ${type}`) - .filter((type, i, arr) => type != null && arr.indexOf(type) === i) - .join(' OR ')})` - : `alert.attributes.alertTypeId: ${SIGNALS_ID}`; +export const getFilter = (filter: string | null | undefined) => { + const alertTypeFilter = `(${Object.values(ruleTypeMappings) + .map((type) => `alert.attributes.alertTypeId: ${type}`) + .filter((type, i, arr) => type != null && arr.indexOf(type) === i) + .join(' OR ')})`; if (filter == null) { return alertTypeFilter; } else { @@ -36,14 +31,13 @@ export const findRules = ({ filter, sortField, sortOrder, - isRuleRegistryEnabled, }: FindRuleOptions): Promise> => { return rulesClient.find({ options: { fields, page, perPage, - filter: getFilter(filter, isRuleRegistryEnabled), + filter: getFilter(filter), sortOrder, sortField, }, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_existing_prepackaged_rules.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_existing_prepackaged_rules.test.ts index 7ddf4c442728..279a211c9ea3 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_existing_prepackaged_rules.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_existing_prepackaged_rules.test.ts @@ -7,7 +7,7 @@ import { rulesClientMock } from '@kbn/alerting-plugin/server/mocks'; import { - getAlertMock, + getRuleMock, getFindResultWithSingleHit, getFindResultWithMultiHits, } from '../routes/__mocks__/request_responses'; @@ -20,10 +20,7 @@ import { getNonPackagedRulesCount, } from './get_existing_prepackaged_rules'; -describe.each([ - ['Legacy', false], - ['RAC', true], -])('get_existing_prepackaged_rules - %s', (_, isRuleRegistryEnabled) => { +describe('get_existing_prepackaged_rules', () => { afterEach(() => { jest.resetAllMocks(); }); @@ -31,23 +28,23 @@ describe.each([ describe('getExistingPrepackagedRules', () => { test('should return a single item in a single page', async () => { const rulesClient = rulesClientMock.create(); - rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled)); - const rules = await getExistingPrepackagedRules({ isRuleRegistryEnabled, rulesClient }); - expect(rules).toEqual([getAlertMock(isRuleRegistryEnabled, getQueryRuleParams())]); + rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); + const rules = await getExistingPrepackagedRules({ rulesClient }); + expect(rules).toEqual([getRuleMock(getQueryRuleParams())]); }); test('should return 3 items over 1 page with all on one page', async () => { const rulesClient = rulesClientMock.create(); - const result1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const result1 = getRuleMock(getQueryRuleParams()); result1.params.immutable = true; result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d'; - const result2 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const result2 = getRuleMock(getQueryRuleParams()); result2.params.immutable = true; result2.id = '5baa53f8-96da-44ee-ad58-41bccb7f9f3d'; - const result3 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const result3 = getRuleMock(getQueryRuleParams()); result3.params.immutable = true; result3.id = 'f3e1bf0b-b95f-43da-b1de-5d2f0af2287a'; @@ -71,7 +68,7 @@ describe.each([ }) ); - const rules = await getExistingPrepackagedRules({ isRuleRegistryEnabled, rulesClient }); + const rules = await getExistingPrepackagedRules({ rulesClient }); expect(rules).toEqual([result1, result2, result3]); }); }); @@ -79,18 +76,18 @@ describe.each([ describe('getNonPackagedRules', () => { test('should return a single item in a single page', async () => { const rulesClient = rulesClientMock.create(); - rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled)); - const rules = await getNonPackagedRules({ isRuleRegistryEnabled, rulesClient }); - expect(rules).toEqual([getAlertMock(isRuleRegistryEnabled, getQueryRuleParams())]); + rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); + const rules = await getNonPackagedRules({ rulesClient }); + expect(rules).toEqual([getRuleMock(getQueryRuleParams())]); }); test('should return 2 items over 1 page', async () => { const rulesClient = rulesClientMock.create(); - const result1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const result1 = getRuleMock(getQueryRuleParams()); result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d'; - const result2 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const result2 = getRuleMock(getQueryRuleParams()); result2.id = '5baa53f8-96da-44ee-ad58-41bccb7f9f3d'; // first result mock which is for returning the total @@ -108,20 +105,20 @@ describe.each([ getFindResultWithMultiHits({ data: [result1, result2], perPage: 2, page: 1, total: 2 }) ); - const rules = await getNonPackagedRules({ isRuleRegistryEnabled, rulesClient }); + const rules = await getNonPackagedRules({ rulesClient }); expect(rules).toEqual([result1, result2]); }); test('should return 3 items over 1 page with all on one page', async () => { const rulesClient = rulesClientMock.create(); - const result1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const result1 = getRuleMock(getQueryRuleParams()); result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d'; - const result2 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const result2 = getRuleMock(getQueryRuleParams()); result2.id = '5baa53f8-96da-44ee-ad58-41bccb7f9f3d'; - const result3 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const result3 = getRuleMock(getQueryRuleParams()); result3.id = 'f3e1bf0b-b95f-43da-b1de-5d2f0af2287a'; // first result mock which is for returning the total @@ -144,7 +141,7 @@ describe.each([ }) ); - const rules = await getNonPackagedRules({ isRuleRegistryEnabled, rulesClient }); + const rules = await getNonPackagedRules({ rulesClient }); expect(rules).toEqual([result1, result2, result3]); }); }); @@ -152,18 +149,18 @@ describe.each([ describe('getRules', () => { test('should return a single item in a single page', async () => { const rulesClient = rulesClientMock.create(); - rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled)); - const rules = await getRules({ isRuleRegistryEnabled, rulesClient, filter: '' }); - expect(rules).toEqual([getAlertMock(isRuleRegistryEnabled, getQueryRuleParams())]); + rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); + const rules = await getRules({ rulesClient, filter: '' }); + expect(rules).toEqual([getRuleMock(getQueryRuleParams())]); }); test('should return 2 items over two pages, one per page', async () => { const rulesClient = rulesClientMock.create(); - const result1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const result1 = getRuleMock(getQueryRuleParams()); result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d'; - const result2 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const result2 = getRuleMock(getQueryRuleParams()); result2.id = '5baa53f8-96da-44ee-ad58-41bccb7f9f3d'; // first result mock which is for returning the total @@ -181,7 +178,7 @@ describe.each([ getFindResultWithMultiHits({ data: [result1, result2], perPage: 2, page: 1, total: 2 }) ); - const rules = await getRules({ isRuleRegistryEnabled, rulesClient, filter: '' }); + const rules = await getRules({ rulesClient, filter: '' }); expect(rules).toEqual([result1, result2]); }); }); @@ -189,8 +186,8 @@ describe.each([ describe('getRulesCount', () => { test('it returns a count', async () => { const rulesClient = rulesClientMock.create(); - rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled)); - const rules = await getRulesCount({ isRuleRegistryEnabled, rulesClient, filter: '' }); + rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); + const rules = await getRulesCount({ rulesClient, filter: '' }); expect(rules).toEqual(1); }); }); @@ -198,8 +195,8 @@ describe.each([ describe('getNonPackagedRulesCount', () => { test('it returns a count', async () => { const rulesClient = rulesClientMock.create(); - rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled)); - const rules = await getNonPackagedRulesCount({ isRuleRegistryEnabled, rulesClient }); + rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); + const rules = await getNonPackagedRulesCount({ rulesClient }); expect(rules).toEqual(1); }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_existing_prepackaged_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_existing_prepackaged_rules.ts index 68a0c077e59a..a15cc1d3eec1 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_existing_prepackaged_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_existing_prepackaged_rules.ts @@ -14,26 +14,21 @@ export const FILTER_NON_PREPACKED_RULES = `alert.attributes.tags: "${INTERNAL_IM export const FILTER_PREPACKED_RULES = `alert.attributes.tags: "${INTERNAL_IMMUTABLE_KEY}:true"`; export const getNonPackagedRulesCount = async ({ - isRuleRegistryEnabled, rulesClient, }: { - isRuleRegistryEnabled: boolean; rulesClient: RulesClient; }): Promise => { - return getRulesCount({ isRuleRegistryEnabled, rulesClient, filter: FILTER_NON_PREPACKED_RULES }); + return getRulesCount({ rulesClient, filter: FILTER_NON_PREPACKED_RULES }); }; export const getRulesCount = async ({ rulesClient, filter, - isRuleRegistryEnabled, }: { rulesClient: RulesClient; filter: string; - isRuleRegistryEnabled: boolean; }): Promise => { const firstRule = await findRules({ - isRuleRegistryEnabled, rulesClient, filter, perPage: 1, @@ -48,15 +43,12 @@ export const getRulesCount = async ({ export const getRules = async ({ rulesClient, filter, - isRuleRegistryEnabled, }: { rulesClient: RulesClient; filter: string; - isRuleRegistryEnabled: boolean; }) => { - const count = await getRulesCount({ rulesClient, filter, isRuleRegistryEnabled }); + const count = await getRulesCount({ rulesClient, filter }); const rules = await findRules({ - isRuleRegistryEnabled, rulesClient, filter, perPage: count, @@ -66,7 +58,7 @@ export const getRules = async ({ fields: undefined, }); - if (isAlertTypes(isRuleRegistryEnabled, rules.data)) { + if (isAlertTypes(rules.data)) { return rules.data; } else { // If this was ever true, you have a really messed up system. @@ -77,28 +69,22 @@ export const getRules = async ({ export const getNonPackagedRules = async ({ rulesClient, - isRuleRegistryEnabled, }: { rulesClient: RulesClient; - isRuleRegistryEnabled: boolean; }): Promise => { return getRules({ rulesClient, filter: FILTER_NON_PREPACKED_RULES, - isRuleRegistryEnabled, }); }; export const getExistingPrepackagedRules = async ({ rulesClient, - isRuleRegistryEnabled, }: { rulesClient: RulesClient; - isRuleRegistryEnabled: boolean; }): Promise => { return getRules({ rulesClient, filter: FILTER_PREPACKED_RULES, - isRuleRegistryEnabled, }); }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_all.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_all.test.ts index b42508b0a73b..de80a8ba8c26 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_all.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_all.test.ts @@ -6,7 +6,7 @@ */ import { - getAlertMock, + getRuleMock, getFindResultWithSingleHit, FindHit, getEmptySavedObjectsResponse, @@ -27,10 +27,7 @@ import { requestContextMock } from '../routes/__mocks__/request_context'; const exceptionsClient = getExceptionListClientMock(); -describe.each([ - ['Legacy', false], - ['RAC', true], -])('getExportAll - %s', (_, isRuleRegistryEnabled) => { +describe('getExportAll', () => { let logger: ReturnType; const { clients } = requestContextMock.createTools(); @@ -40,8 +37,8 @@ describe.each([ test('it exports everything from the alerts client', async () => { const rulesClient = rulesClientMock.create(); - const result = getFindResultWithSingleHit(isRuleRegistryEnabled); - const alert = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const result = getFindResultWithSingleHit(); + const alert = getRuleMock(getQueryRuleParams()); alert.params = { ...alert.params, @@ -58,8 +55,7 @@ describe.each([ rulesClient, exceptionsClient, clients.savedObjectsClient, - logger, - isRuleRegistryEnabled + logger ); const rulesJson = JSON.parse(exports.rulesNdjson); const detailsJson = JSON.parse(exports.exportDetails); @@ -134,8 +130,7 @@ describe.each([ rulesClient, exceptionsClient, clients.savedObjectsClient, - logger, - isRuleRegistryEnabled + logger ); expect(exports).toEqual({ rulesNdjson: '', diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_all.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_all.ts index 3c2d516b8369..cf1a9991b319 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_all.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_all.ts @@ -22,14 +22,13 @@ export const getExportAll = async ( rulesClient: RulesClient, exceptionsClient: ExceptionListClient | undefined, savedObjectsClient: RuleExecutorServices['savedObjectsClient'], - logger: Logger, - isRuleRegistryEnabled: boolean + logger: Logger ): Promise<{ rulesNdjson: string; exportDetails: string; exceptionLists: string | null; }> => { - const ruleAlertTypes = await getNonPackagedRules({ rulesClient, isRuleRegistryEnabled }); + const ruleAlertTypes = await getNonPackagedRules({ rulesClient }); const alertIds = ruleAlertTypes.map((rule) => rule.id); // Gather actions diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_by_object_ids.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_by_object_ids.test.ts index 7371f2390478..f297f375dda0 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_by_object_ids.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_by_object_ids.test.ts @@ -7,7 +7,7 @@ import { getExportByObjectIds, getRulesFromObjects, RulesErrors } from './get_export_by_object_ids'; import { - getAlertMock, + getRuleMock, getFindResultWithSingleHit, FindHit, getEmptySavedObjectsResponse, @@ -26,10 +26,7 @@ const exceptionsClient = getExceptionListClientMock(); import { loggingSystemMock } from '@kbn/core/server/mocks'; import { requestContextMock } from '../routes/__mocks__/request_context'; -describe.each([ - ['Legacy', false], - ['RAC', true], -])('get_export_by_object_ids - %s', (_, isRuleRegistryEnabled) => { +describe('get_export_by_object_ids', () => { let logger: ReturnType; const { clients } = requestContextMock.createTools(); @@ -44,7 +41,7 @@ describe.each([ describe('getExportByObjectIds', () => { test('it exports object ids into an expected string with new line characters', async () => { const rulesClient = rulesClientMock.create(); - rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled)); + rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); const objects = [{ rule_id: 'rule-1' }]; const exports = await getExportByObjectIds( @@ -52,8 +49,7 @@ describe.each([ exceptionsClient, clients.savedObjectsClient, objects, - logger, - isRuleRegistryEnabled + logger ); const exportsObj = { rulesNdjson: JSON.parse(exports.rulesNdjson), @@ -118,7 +114,7 @@ describe.each([ test('it does not export immutable rules', async () => { const rulesClient = rulesClientMock.create(); - const result = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const result = getRuleMock(getQueryRuleParams()); result.params.immutable = true; const findResult: FindHit = { @@ -128,7 +124,7 @@ describe.each([ data: [result], }; - rulesClient.get.mockResolvedValue(getAlertMock(isRuleRegistryEnabled, getQueryRuleParams())); + rulesClient.get.mockResolvedValue(getRuleMock(getQueryRuleParams())); rulesClient.find.mockResolvedValue(findResult); const objects = [{ rule_id: 'rule-1' }]; @@ -137,8 +133,7 @@ describe.each([ exceptionsClient, clients.savedObjectsClient, objects, - logger, - isRuleRegistryEnabled + logger ); const details = getOutputDetailsSampleWithExceptions({ missingRules: [{ rule_id: 'rule-1' }], @@ -155,15 +150,14 @@ describe.each([ describe('getRulesFromObjects', () => { test('it returns transformed rules from objects sent in', async () => { const rulesClient = rulesClientMock.create(); - rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled)); + rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); const objects = [{ rule_id: 'rule-1' }]; const exports = await getRulesFromObjects( rulesClient, clients.savedObjectsClient, objects, - logger, - isRuleRegistryEnabled + logger ); const expected: RulesErrors = { exportedCount: 1, @@ -220,7 +214,7 @@ describe.each([ test('it does not transform the rule if the rule is an immutable rule and designates it as a missing rule', async () => { const rulesClient = rulesClientMock.create(); - const result = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const result = getRuleMock(getQueryRuleParams()); result.params.immutable = true; const findResult: FindHit = { @@ -238,8 +232,7 @@ describe.each([ rulesClient, clients.savedObjectsClient, objects, - logger, - isRuleRegistryEnabled + logger ); const expected: RulesErrors = { exportedCount: 0, @@ -267,8 +260,7 @@ describe.each([ rulesClient, clients.savedObjectsClient, objects, - logger, - isRuleRegistryEnabled + logger ); const expected: RulesErrors = { exportedCount: 0, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_by_object_ids.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_by_object_ids.ts index 5dde3048b61e..836c9b035f30 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_by_object_ids.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_by_object_ids.ts @@ -45,8 +45,7 @@ export const getExportByObjectIds = async ( exceptionsClient: ExceptionListClient | undefined, savedObjectsClient: RuleExecutorServices['savedObjectsClient'], objects: Array<{ rule_id: string }>, - logger: Logger, - isRuleRegistryEnabled: boolean + logger: Logger ): Promise<{ rulesNdjson: string; exportDetails: string; @@ -56,8 +55,7 @@ export const getExportByObjectIds = async ( rulesClient, savedObjectsClient, objects, - logger, - isRuleRegistryEnabled + logger ); // Retrieve exceptions @@ -83,8 +81,7 @@ export const getRulesFromObjects = async ( rulesClient: RulesClient, savedObjectsClient: RuleExecutorServices['savedObjectsClient'], objects: Array<{ rule_id: string }>, - logger: Logger, - isRuleRegistryEnabled: boolean + logger: Logger ): Promise => { // If we put more than 1024 ids in one block like "alert.attributes.tags: (id1 OR id2 OR ... OR id1100)" // then the KQL -> ES DSL query generator still puts them all in the same "should" array, but ES defaults @@ -102,7 +99,6 @@ export const getRulesFromObjects = async ( }) .join(' OR '); const rules = await findRules({ - isRuleRegistryEnabled, rulesClient, filter, page: 1, @@ -122,7 +118,7 @@ export const getRulesFromObjects = async ( const matchingRule = rules.data.find((rule) => rule.params.ruleId === ruleId); if ( matchingRule != null && - isAlertType(isRuleRegistryEnabled, matchingRule) && + isAlertType(matchingRule) && matchingRule.params.immutable !== true ) { return { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_rules_to_install.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_rules_to_install.test.ts index c5d4d6d5d2d4..5fcc0aec0f15 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_rules_to_install.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_rules_to_install.test.ts @@ -6,15 +6,12 @@ */ import { getRulesToInstall } from './get_rules_to_install'; -import { getAlertMock } from '../routes/__mocks__/request_responses'; +import { getRuleMock } from '../routes/__mocks__/request_responses'; import { getAddPrepackagedRulesSchemaDecodedMock } from '../../../../common/detection_engine/schemas/request/add_prepackaged_rules_schema.mock'; import { getQueryRuleParams } from '../schemas/rule_schemas.mock'; import { AddPrepackagedRulesSchemaDecoded } from '../../../../common/detection_engine/schemas/request'; -describe.each([ - ['Legacy', false], - ['RAC', true], -])('get_rules_to_install - %s', (_, isRuleRegistryEnabled) => { +describe('get_rules_to_install', () => { test('should return empty array if both rule sets are empty', () => { const update = getRulesToInstall([], []); expect(update).toEqual([]); @@ -25,7 +22,7 @@ describe.each([ getAddPrepackagedRulesSchemaDecodedMock() as AddPrepackagedRulesSchemaDecoded; ruleFromFileSystem.rule_id = 'rule-1'; - const installedRule = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const installedRule = getRuleMock(getQueryRuleParams()); installedRule.params.ruleId = 'rule-1'; const update = getRulesToInstall([ruleFromFileSystem], [installedRule]); expect(update).toEqual([]); @@ -36,7 +33,7 @@ describe.each([ getAddPrepackagedRulesSchemaDecodedMock() as AddPrepackagedRulesSchemaDecoded; ruleFromFileSystem.rule_id = 'rule-1'; - const installedRule = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const installedRule = getRuleMock(getQueryRuleParams()); installedRule.params.ruleId = 'rule-2'; const update = getRulesToInstall([ruleFromFileSystem], [installedRule]); expect(update).toEqual([ruleFromFileSystem]); @@ -51,7 +48,7 @@ describe.each([ getAddPrepackagedRulesSchemaDecodedMock() as AddPrepackagedRulesSchemaDecoded; ruleFromFileSystem2.rule_id = 'rule-2'; - const installedRule = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const installedRule = getRuleMock(getQueryRuleParams()); installedRule.params.ruleId = 'rule-3'; const update = getRulesToInstall([ruleFromFileSystem1, ruleFromFileSystem2], [installedRule]); expect(update).toEqual([ruleFromFileSystem1, ruleFromFileSystem2]); @@ -70,7 +67,7 @@ describe.each([ getAddPrepackagedRulesSchemaDecodedMock() as AddPrepackagedRulesSchemaDecoded; ruleFromFileSystem3.rule_id = 'rule-3'; - const installedRule = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const installedRule = getRuleMock(getQueryRuleParams()); installedRule.params.ruleId = 'rule-3'; const update = getRulesToInstall( [ruleFromFileSystem1, ruleFromFileSystem2, ruleFromFileSystem3], diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_rules_to_update.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_rules_to_update.test.ts index dda3bf6efe44..a1d845848848 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_rules_to_update.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_rules_to_update.test.ts @@ -6,473 +6,468 @@ */ import { filterInstalledRules, getRulesToUpdate, mergeExceptionLists } from './get_rules_to_update'; -import { getAlertMock } from '../routes/__mocks__/request_responses'; +import { getRuleMock } from '../routes/__mocks__/request_responses'; import { getAddPrepackagedRulesSchemaDecodedMock } from '../../../../common/detection_engine/schemas/request/add_prepackaged_rules_schema.mock'; import { getQueryRuleParams } from '../schemas/rule_schemas.mock'; -describe.each([ - ['Legacy', false], - ['RAC', true], -])('get_rules_to_update - %s', (_, isRuleRegistryEnabled) => { - describe('get_rules_to_update', () => { - test('should return empty array if both rule sets are empty', () => { - const update = getRulesToUpdate([], []); - expect(update).toEqual([]); - }); - - test('should return empty array if the rule_id of the two rules do not match', () => { - const ruleFromFileSystem = getAddPrepackagedRulesSchemaDecodedMock(); - ruleFromFileSystem.rule_id = 'rule-1'; - ruleFromFileSystem.version = 2; - - const installedRule = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); - installedRule.params.ruleId = 'rule-2'; - installedRule.params.version = 1; - const update = getRulesToUpdate([ruleFromFileSystem], [installedRule]); - expect(update).toEqual([]); - }); - - test('should return empty array if the version of file system rule is less than the installed version', () => { - const ruleFromFileSystem = getAddPrepackagedRulesSchemaDecodedMock(); - ruleFromFileSystem.rule_id = 'rule-1'; - ruleFromFileSystem.version = 1; - - const installedRule = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); - installedRule.params.ruleId = 'rule-1'; - installedRule.params.version = 2; - const update = getRulesToUpdate([ruleFromFileSystem], [installedRule]); - expect(update).toEqual([]); - }); - - test('should return empty array if the version of file system rule is the same as the installed version', () => { - const ruleFromFileSystem = getAddPrepackagedRulesSchemaDecodedMock(); - ruleFromFileSystem.rule_id = 'rule-1'; - ruleFromFileSystem.version = 1; - - const installedRule = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); - installedRule.params.ruleId = 'rule-1'; - installedRule.params.version = 1; - const update = getRulesToUpdate([ruleFromFileSystem], [installedRule]); - expect(update).toEqual([]); - }); - - test('should return the rule to update if the version of file system rule is greater than the installed version', () => { - const ruleFromFileSystem = getAddPrepackagedRulesSchemaDecodedMock(); - ruleFromFileSystem.rule_id = 'rule-1'; - ruleFromFileSystem.version = 2; - - const installedRule = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); - installedRule.params.ruleId = 'rule-1'; - installedRule.params.version = 1; - installedRule.params.exceptionsList = []; - - const update = getRulesToUpdate([ruleFromFileSystem], [installedRule]); - expect(update).toEqual([ruleFromFileSystem]); - }); - - test('should return 1 rule out of 2 to update if the version of file system rule is greater than the installed version of just one', () => { - const ruleFromFileSystem = getAddPrepackagedRulesSchemaDecodedMock(); - ruleFromFileSystem.rule_id = 'rule-1'; - ruleFromFileSystem.version = 2; - - const installedRule1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); - installedRule1.params.ruleId = 'rule-1'; - installedRule1.params.version = 1; - installedRule1.params.exceptionsList = []; - - const installedRule2 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); - installedRule2.params.ruleId = 'rule-2'; - installedRule2.params.version = 1; - installedRule2.params.exceptionsList = []; - - const update = getRulesToUpdate([ruleFromFileSystem], [installedRule1, installedRule2]); - expect(update).toEqual([ruleFromFileSystem]); - }); - - test('should return 2 rules out of 2 to update if the version of file system rule is greater than the installed version of both', () => { - const ruleFromFileSystem1 = getAddPrepackagedRulesSchemaDecodedMock(); - ruleFromFileSystem1.rule_id = 'rule-1'; - ruleFromFileSystem1.version = 2; - - const ruleFromFileSystem2 = getAddPrepackagedRulesSchemaDecodedMock(); - ruleFromFileSystem2.rule_id = 'rule-2'; - ruleFromFileSystem2.version = 2; - - const installedRule1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); - installedRule1.params.ruleId = 'rule-1'; - installedRule1.params.version = 1; - installedRule1.params.exceptionsList = []; - - const installedRule2 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); - installedRule2.params.ruleId = 'rule-2'; - installedRule2.params.version = 1; - installedRule2.params.exceptionsList = []; - - const update = getRulesToUpdate( - [ruleFromFileSystem1, ruleFromFileSystem2], - [installedRule1, installedRule2] - ); - expect(update).toEqual([ruleFromFileSystem1, ruleFromFileSystem2]); - }); - - test('should add back an exception_list if it was removed by the end user on an immutable rule during an upgrade', () => { - const ruleFromFileSystem1 = getAddPrepackagedRulesSchemaDecodedMock(); - ruleFromFileSystem1.exceptions_list = [ - { - id: 'endpoint_list', - list_id: 'endpoint_list', - namespace_type: 'agnostic', - type: 'endpoint', - }, - ]; - ruleFromFileSystem1.rule_id = 'rule-1'; - ruleFromFileSystem1.version = 2; - - const installedRule1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); - installedRule1.params.ruleId = 'rule-1'; - installedRule1.params.version = 1; - installedRule1.params.exceptionsList = []; - - const [update] = getRulesToUpdate([ruleFromFileSystem1], [installedRule1]); - expect(update.exceptions_list).toEqual(ruleFromFileSystem1.exceptions_list); - }); - - test('should not remove an additional exception_list if an additional one was added by the end user on an immutable rule during an upgrade', () => { - const ruleFromFileSystem1 = getAddPrepackagedRulesSchemaDecodedMock(); - ruleFromFileSystem1.exceptions_list = [ - { - id: 'endpoint_list', - list_id: 'endpoint_list', - namespace_type: 'agnostic', - type: 'endpoint', - }, - ]; - ruleFromFileSystem1.rule_id = 'rule-1'; - ruleFromFileSystem1.version = 2; - - const installedRule1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); - installedRule1.params.ruleId = 'rule-1'; - installedRule1.params.version = 1; - installedRule1.params.exceptionsList = [ - { - id: 'second_exception_list', - list_id: 'some-other-id', - namespace_type: 'single', - type: 'detection', - }, - ]; - - const [update] = getRulesToUpdate([ruleFromFileSystem1], [installedRule1]); - expect(update.exceptions_list).toEqual([ - ...ruleFromFileSystem1.exceptions_list, - ...installedRule1.params.exceptionsList, - ]); - }); - - test('should not remove an existing exception_list if they are the same between the current installed one and the upgraded one', () => { - const ruleFromFileSystem1 = getAddPrepackagedRulesSchemaDecodedMock(); - ruleFromFileSystem1.exceptions_list = [ - { - id: 'endpoint_list', - list_id: 'endpoint_list', - namespace_type: 'agnostic', - type: 'endpoint', - }, - ]; - ruleFromFileSystem1.rule_id = 'rule-1'; - ruleFromFileSystem1.version = 2; - - const installedRule1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); - installedRule1.params.ruleId = 'rule-1'; - installedRule1.params.version = 1; - installedRule1.params.exceptionsList = [ - { - id: 'endpoint_list', - list_id: 'endpoint_list', - namespace_type: 'agnostic', - type: 'endpoint', - }, - ]; - - const [update] = getRulesToUpdate([ruleFromFileSystem1], [installedRule1]); - expect(update.exceptions_list).toEqual(ruleFromFileSystem1.exceptions_list); - }); - - test('should not remove an existing exception_list if the rule has an empty exceptions list', () => { - const ruleFromFileSystem1 = getAddPrepackagedRulesSchemaDecodedMock(); - ruleFromFileSystem1.exceptions_list = []; - ruleFromFileSystem1.rule_id = 'rule-1'; - ruleFromFileSystem1.version = 2; - - const installedRule1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); - installedRule1.params.ruleId = 'rule-1'; - installedRule1.params.version = 1; - installedRule1.params.exceptionsList = [ - { - id: 'endpoint_list', - list_id: 'endpoint_list', - namespace_type: 'agnostic', - type: 'endpoint', - }, - ]; - - const [update] = getRulesToUpdate([ruleFromFileSystem1], [installedRule1]); - expect(update.exceptions_list).toEqual(installedRule1.params.exceptionsList); - }); - - test('should not remove an existing exception_list if the rule has an empty exceptions list for multiple rules', () => { - const ruleFromFileSystem1 = getAddPrepackagedRulesSchemaDecodedMock(); - ruleFromFileSystem1.exceptions_list = []; - ruleFromFileSystem1.rule_id = 'rule-1'; - ruleFromFileSystem1.version = 2; - - const ruleFromFileSystem2 = getAddPrepackagedRulesSchemaDecodedMock(); - ruleFromFileSystem2.exceptions_list = []; - ruleFromFileSystem2.rule_id = 'rule-2'; - ruleFromFileSystem2.version = 2; - - const installedRule1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); - installedRule1.params.ruleId = 'rule-1'; - installedRule1.params.version = 1; - installedRule1.params.exceptionsList = [ - { - id: 'endpoint_list', - list_id: 'endpoint_list', - namespace_type: 'agnostic', - type: 'endpoint', - }, - ]; - const installedRule2 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); - installedRule2.params.ruleId = 'rule-2'; - installedRule2.params.version = 1; - installedRule2.params.exceptionsList = [ - { - id: 'endpoint_list', - list_id: 'endpoint_list', - namespace_type: 'agnostic', - type: 'endpoint', - }, - ]; - - const [update1, update2] = getRulesToUpdate( - [ruleFromFileSystem1, ruleFromFileSystem2], - [installedRule1, installedRule2] - ); - expect(update1.exceptions_list).toEqual(installedRule1.params.exceptionsList); - expect(update2.exceptions_list).toEqual(installedRule2.params.exceptionsList); - }); - - test('should not remove an existing exception_list if the rule has an empty exceptions list for mixed rules', () => { - const ruleFromFileSystem1 = getAddPrepackagedRulesSchemaDecodedMock(); - ruleFromFileSystem1.exceptions_list = []; - ruleFromFileSystem1.rule_id = 'rule-1'; - ruleFromFileSystem1.version = 2; - - const ruleFromFileSystem2 = getAddPrepackagedRulesSchemaDecodedMock(); - ruleFromFileSystem2.exceptions_list = []; - ruleFromFileSystem2.rule_id = 'rule-2'; - ruleFromFileSystem2.version = 2; - ruleFromFileSystem2.exceptions_list = [ - { - id: 'second_list', - list_id: 'second_list', - namespace_type: 'single', - type: 'detection', - }, - ]; - - const installedRule1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); - installedRule1.params.ruleId = 'rule-1'; - installedRule1.params.version = 1; - installedRule1.params.exceptionsList = [ - { - id: 'endpoint_list', - list_id: 'endpoint_list', - namespace_type: 'agnostic', - type: 'endpoint', - }, - ]; - - const installedRule2 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); - installedRule2.params.ruleId = 'rule-2'; - installedRule2.params.version = 1; - installedRule2.params.exceptionsList = [ - { - id: 'endpoint_list', - list_id: 'endpoint_list', - namespace_type: 'agnostic', - type: 'endpoint', - }, - ]; - - const [update1, update2] = getRulesToUpdate( - [ruleFromFileSystem1, ruleFromFileSystem2], - [installedRule1, installedRule2] - ); - expect(update1.exceptions_list).toEqual(installedRule1.params.exceptionsList); - expect(update2.exceptions_list).toEqual([ - ...ruleFromFileSystem2.exceptions_list, - ...installedRule2.params.exceptionsList, - ]); - }); +describe('get_rules_to_update', () => { + test('should return empty array if both rule sets are empty', () => { + const update = getRulesToUpdate([], []); + expect(update).toEqual([]); }); - describe('filterInstalledRules', () => { - test('should return "false" if the id of the two rules do not match', () => { - const ruleFromFileSystem = getAddPrepackagedRulesSchemaDecodedMock(); - ruleFromFileSystem.rule_id = 'rule-1'; - ruleFromFileSystem.version = 2; - - const installedRule = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); - installedRule.params.ruleId = 'rule-2'; - installedRule.params.version = 1; - const shouldUpdate = filterInstalledRules(ruleFromFileSystem, [installedRule]); - expect(shouldUpdate).toEqual(false); - }); - - test('should return "false" if the version of file system rule is less than the installed version', () => { - const ruleFromFileSystem = getAddPrepackagedRulesSchemaDecodedMock(); - ruleFromFileSystem.rule_id = 'rule-1'; - ruleFromFileSystem.version = 1; - - const installedRule = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); - installedRule.params.ruleId = 'rule-1'; - installedRule.params.version = 2; - const shouldUpdate = filterInstalledRules(ruleFromFileSystem, [installedRule]); - expect(shouldUpdate).toEqual(false); - }); - - test('should return "false" if the version of file system rule is the same as the installed version', () => { - const ruleFromFileSystem = getAddPrepackagedRulesSchemaDecodedMock(); - ruleFromFileSystem.rule_id = 'rule-1'; - ruleFromFileSystem.version = 1; - - const installedRule = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); - installedRule.params.ruleId = 'rule-1'; - installedRule.params.version = 1; - const shouldUpdate = filterInstalledRules(ruleFromFileSystem, [installedRule]); - expect(shouldUpdate).toEqual(false); - }); - - test('should return "true" to update if the version of file system rule is greater than the installed version', () => { - const ruleFromFileSystem = getAddPrepackagedRulesSchemaDecodedMock(); - ruleFromFileSystem.rule_id = 'rule-1'; - ruleFromFileSystem.version = 2; - - const installedRule = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); - installedRule.params.ruleId = 'rule-1'; - installedRule.params.version = 1; - installedRule.params.exceptionsList = []; - - const shouldUpdate = filterInstalledRules(ruleFromFileSystem, [installedRule]); - expect(shouldUpdate).toEqual(true); - }); + test('should return empty array if the rule_id of the two rules do not match', () => { + const ruleFromFileSystem = getAddPrepackagedRulesSchemaDecodedMock(); + ruleFromFileSystem.rule_id = 'rule-1'; + ruleFromFileSystem.version = 2; + + const installedRule = getRuleMock(getQueryRuleParams()); + installedRule.params.ruleId = 'rule-2'; + installedRule.params.version = 1; + const update = getRulesToUpdate([ruleFromFileSystem], [installedRule]); + expect(update).toEqual([]); + }); + + test('should return empty array if the version of file system rule is less than the installed version', () => { + const ruleFromFileSystem = getAddPrepackagedRulesSchemaDecodedMock(); + ruleFromFileSystem.rule_id = 'rule-1'; + ruleFromFileSystem.version = 1; + + const installedRule = getRuleMock(getQueryRuleParams()); + installedRule.params.ruleId = 'rule-1'; + installedRule.params.version = 2; + const update = getRulesToUpdate([ruleFromFileSystem], [installedRule]); + expect(update).toEqual([]); + }); + + test('should return empty array if the version of file system rule is the same as the installed version', () => { + const ruleFromFileSystem = getAddPrepackagedRulesSchemaDecodedMock(); + ruleFromFileSystem.rule_id = 'rule-1'; + ruleFromFileSystem.version = 1; + + const installedRule = getRuleMock(getQueryRuleParams()); + installedRule.params.ruleId = 'rule-1'; + installedRule.params.version = 1; + const update = getRulesToUpdate([ruleFromFileSystem], [installedRule]); + expect(update).toEqual([]); + }); + + test('should return the rule to update if the version of file system rule is greater than the installed version', () => { + const ruleFromFileSystem = getAddPrepackagedRulesSchemaDecodedMock(); + ruleFromFileSystem.rule_id = 'rule-1'; + ruleFromFileSystem.version = 2; + + const installedRule = getRuleMock(getQueryRuleParams()); + installedRule.params.ruleId = 'rule-1'; + installedRule.params.version = 1; + installedRule.params.exceptionsList = []; + + const update = getRulesToUpdate([ruleFromFileSystem], [installedRule]); + expect(update).toEqual([ruleFromFileSystem]); + }); + + test('should return 1 rule out of 2 to update if the version of file system rule is greater than the installed version of just one', () => { + const ruleFromFileSystem = getAddPrepackagedRulesSchemaDecodedMock(); + ruleFromFileSystem.rule_id = 'rule-1'; + ruleFromFileSystem.version = 2; + + const installedRule1 = getRuleMock(getQueryRuleParams()); + installedRule1.params.ruleId = 'rule-1'; + installedRule1.params.version = 1; + installedRule1.params.exceptionsList = []; + + const installedRule2 = getRuleMock(getQueryRuleParams()); + installedRule2.params.ruleId = 'rule-2'; + installedRule2.params.version = 1; + installedRule2.params.exceptionsList = []; + + const update = getRulesToUpdate([ruleFromFileSystem], [installedRule1, installedRule2]); + expect(update).toEqual([ruleFromFileSystem]); + }); + + test('should return 2 rules out of 2 to update if the version of file system rule is greater than the installed version of both', () => { + const ruleFromFileSystem1 = getAddPrepackagedRulesSchemaDecodedMock(); + ruleFromFileSystem1.rule_id = 'rule-1'; + ruleFromFileSystem1.version = 2; + + const ruleFromFileSystem2 = getAddPrepackagedRulesSchemaDecodedMock(); + ruleFromFileSystem2.rule_id = 'rule-2'; + ruleFromFileSystem2.version = 2; + + const installedRule1 = getRuleMock(getQueryRuleParams()); + installedRule1.params.ruleId = 'rule-1'; + installedRule1.params.version = 1; + installedRule1.params.exceptionsList = []; + + const installedRule2 = getRuleMock(getQueryRuleParams()); + installedRule2.params.ruleId = 'rule-2'; + installedRule2.params.version = 1; + installedRule2.params.exceptionsList = []; + + const update = getRulesToUpdate( + [ruleFromFileSystem1, ruleFromFileSystem2], + [installedRule1, installedRule2] + ); + expect(update).toEqual([ruleFromFileSystem1, ruleFromFileSystem2]); + }); + + test('should add back an exception_list if it was removed by the end user on an immutable rule during an upgrade', () => { + const ruleFromFileSystem1 = getAddPrepackagedRulesSchemaDecodedMock(); + ruleFromFileSystem1.exceptions_list = [ + { + id: 'endpoint_list', + list_id: 'endpoint_list', + namespace_type: 'agnostic', + type: 'endpoint', + }, + ]; + ruleFromFileSystem1.rule_id = 'rule-1'; + ruleFromFileSystem1.version = 2; + + const installedRule1 = getRuleMock(getQueryRuleParams()); + installedRule1.params.ruleId = 'rule-1'; + installedRule1.params.version = 1; + installedRule1.params.exceptionsList = []; + + const [update] = getRulesToUpdate([ruleFromFileSystem1], [installedRule1]); + expect(update.exceptions_list).toEqual(ruleFromFileSystem1.exceptions_list); + }); + + test('should not remove an additional exception_list if an additional one was added by the end user on an immutable rule during an upgrade', () => { + const ruleFromFileSystem1 = getAddPrepackagedRulesSchemaDecodedMock(); + ruleFromFileSystem1.exceptions_list = [ + { + id: 'endpoint_list', + list_id: 'endpoint_list', + namespace_type: 'agnostic', + type: 'endpoint', + }, + ]; + ruleFromFileSystem1.rule_id = 'rule-1'; + ruleFromFileSystem1.version = 2; + + const installedRule1 = getRuleMock(getQueryRuleParams()); + installedRule1.params.ruleId = 'rule-1'; + installedRule1.params.version = 1; + installedRule1.params.exceptionsList = [ + { + id: 'second_exception_list', + list_id: 'some-other-id', + namespace_type: 'single', + type: 'detection', + }, + ]; + + const [update] = getRulesToUpdate([ruleFromFileSystem1], [installedRule1]); + expect(update.exceptions_list).toEqual([ + ...ruleFromFileSystem1.exceptions_list, + ...installedRule1.params.exceptionsList, + ]); + }); + + test('should not remove an existing exception_list if they are the same between the current installed one and the upgraded one', () => { + const ruleFromFileSystem1 = getAddPrepackagedRulesSchemaDecodedMock(); + ruleFromFileSystem1.exceptions_list = [ + { + id: 'endpoint_list', + list_id: 'endpoint_list', + namespace_type: 'agnostic', + type: 'endpoint', + }, + ]; + ruleFromFileSystem1.rule_id = 'rule-1'; + ruleFromFileSystem1.version = 2; + + const installedRule1 = getRuleMock(getQueryRuleParams()); + installedRule1.params.ruleId = 'rule-1'; + installedRule1.params.version = 1; + installedRule1.params.exceptionsList = [ + { + id: 'endpoint_list', + list_id: 'endpoint_list', + namespace_type: 'agnostic', + type: 'endpoint', + }, + ]; + + const [update] = getRulesToUpdate([ruleFromFileSystem1], [installedRule1]); + expect(update.exceptions_list).toEqual(ruleFromFileSystem1.exceptions_list); + }); + + test('should not remove an existing exception_list if the rule has an empty exceptions list', () => { + const ruleFromFileSystem1 = getAddPrepackagedRulesSchemaDecodedMock(); + ruleFromFileSystem1.exceptions_list = []; + ruleFromFileSystem1.rule_id = 'rule-1'; + ruleFromFileSystem1.version = 2; + + const installedRule1 = getRuleMock(getQueryRuleParams()); + installedRule1.params.ruleId = 'rule-1'; + installedRule1.params.version = 1; + installedRule1.params.exceptionsList = [ + { + id: 'endpoint_list', + list_id: 'endpoint_list', + namespace_type: 'agnostic', + type: 'endpoint', + }, + ]; + + const [update] = getRulesToUpdate([ruleFromFileSystem1], [installedRule1]); + expect(update.exceptions_list).toEqual(installedRule1.params.exceptionsList); + }); + + test('should not remove an existing exception_list if the rule has an empty exceptions list for multiple rules', () => { + const ruleFromFileSystem1 = getAddPrepackagedRulesSchemaDecodedMock(); + ruleFromFileSystem1.exceptions_list = []; + ruleFromFileSystem1.rule_id = 'rule-1'; + ruleFromFileSystem1.version = 2; + + const ruleFromFileSystem2 = getAddPrepackagedRulesSchemaDecodedMock(); + ruleFromFileSystem2.exceptions_list = []; + ruleFromFileSystem2.rule_id = 'rule-2'; + ruleFromFileSystem2.version = 2; + + const installedRule1 = getRuleMock(getQueryRuleParams()); + installedRule1.params.ruleId = 'rule-1'; + installedRule1.params.version = 1; + installedRule1.params.exceptionsList = [ + { + id: 'endpoint_list', + list_id: 'endpoint_list', + namespace_type: 'agnostic', + type: 'endpoint', + }, + ]; + const installedRule2 = getRuleMock(getQueryRuleParams()); + installedRule2.params.ruleId = 'rule-2'; + installedRule2.params.version = 1; + installedRule2.params.exceptionsList = [ + { + id: 'endpoint_list', + list_id: 'endpoint_list', + namespace_type: 'agnostic', + type: 'endpoint', + }, + ]; + + const [update1, update2] = getRulesToUpdate( + [ruleFromFileSystem1, ruleFromFileSystem2], + [installedRule1, installedRule2] + ); + expect(update1.exceptions_list).toEqual(installedRule1.params.exceptionsList); + expect(update2.exceptions_list).toEqual(installedRule2.params.exceptionsList); + }); + + test('should not remove an existing exception_list if the rule has an empty exceptions list for mixed rules', () => { + const ruleFromFileSystem1 = getAddPrepackagedRulesSchemaDecodedMock(); + ruleFromFileSystem1.exceptions_list = []; + ruleFromFileSystem1.rule_id = 'rule-1'; + ruleFromFileSystem1.version = 2; + + const ruleFromFileSystem2 = getAddPrepackagedRulesSchemaDecodedMock(); + ruleFromFileSystem2.exceptions_list = []; + ruleFromFileSystem2.rule_id = 'rule-2'; + ruleFromFileSystem2.version = 2; + ruleFromFileSystem2.exceptions_list = [ + { + id: 'second_list', + list_id: 'second_list', + namespace_type: 'single', + type: 'detection', + }, + ]; + + const installedRule1 = getRuleMock(getQueryRuleParams()); + installedRule1.params.ruleId = 'rule-1'; + installedRule1.params.version = 1; + installedRule1.params.exceptionsList = [ + { + id: 'endpoint_list', + list_id: 'endpoint_list', + namespace_type: 'agnostic', + type: 'endpoint', + }, + ]; + + const installedRule2 = getRuleMock(getQueryRuleParams()); + installedRule2.params.ruleId = 'rule-2'; + installedRule2.params.version = 1; + installedRule2.params.exceptionsList = [ + { + id: 'endpoint_list', + list_id: 'endpoint_list', + namespace_type: 'agnostic', + type: 'endpoint', + }, + ]; + + const [update1, update2] = getRulesToUpdate( + [ruleFromFileSystem1, ruleFromFileSystem2], + [installedRule1, installedRule2] + ); + expect(update1.exceptions_list).toEqual(installedRule1.params.exceptionsList); + expect(update2.exceptions_list).toEqual([ + ...ruleFromFileSystem2.exceptions_list, + ...installedRule2.params.exceptionsList, + ]); + }); +}); + +describe('filterInstalledRules', () => { + test('should return "false" if the id of the two rules do not match', () => { + const ruleFromFileSystem = getAddPrepackagedRulesSchemaDecodedMock(); + ruleFromFileSystem.rule_id = 'rule-1'; + ruleFromFileSystem.version = 2; + + const installedRule = getRuleMock(getQueryRuleParams()); + installedRule.params.ruleId = 'rule-2'; + installedRule.params.version = 1; + const shouldUpdate = filterInstalledRules(ruleFromFileSystem, [installedRule]); + expect(shouldUpdate).toEqual(false); + }); + + test('should return "false" if the version of file system rule is less than the installed version', () => { + const ruleFromFileSystem = getAddPrepackagedRulesSchemaDecodedMock(); + ruleFromFileSystem.rule_id = 'rule-1'; + ruleFromFileSystem.version = 1; + + const installedRule = getRuleMock(getQueryRuleParams()); + installedRule.params.ruleId = 'rule-1'; + installedRule.params.version = 2; + const shouldUpdate = filterInstalledRules(ruleFromFileSystem, [installedRule]); + expect(shouldUpdate).toEqual(false); + }); + + test('should return "false" if the version of file system rule is the same as the installed version', () => { + const ruleFromFileSystem = getAddPrepackagedRulesSchemaDecodedMock(); + ruleFromFileSystem.rule_id = 'rule-1'; + ruleFromFileSystem.version = 1; + + const installedRule = getRuleMock(getQueryRuleParams()); + installedRule.params.ruleId = 'rule-1'; + installedRule.params.version = 1; + const shouldUpdate = filterInstalledRules(ruleFromFileSystem, [installedRule]); + expect(shouldUpdate).toEqual(false); + }); + + test('should return "true" to update if the version of file system rule is greater than the installed version', () => { + const ruleFromFileSystem = getAddPrepackagedRulesSchemaDecodedMock(); + ruleFromFileSystem.rule_id = 'rule-1'; + ruleFromFileSystem.version = 2; + + const installedRule = getRuleMock(getQueryRuleParams()); + installedRule.params.ruleId = 'rule-1'; + installedRule.params.version = 1; + installedRule.params.exceptionsList = []; + + const shouldUpdate = filterInstalledRules(ruleFromFileSystem, [installedRule]); + expect(shouldUpdate).toEqual(true); + }); +}); + +describe('mergeExceptionLists', () => { + test('should add back an exception_list if it was removed by the end user on an immutable rule during an upgrade', () => { + const ruleFromFileSystem1 = getAddPrepackagedRulesSchemaDecodedMock(); + ruleFromFileSystem1.exceptions_list = [ + { + id: 'endpoint_list', + list_id: 'endpoint_list', + namespace_type: 'agnostic', + type: 'endpoint', + }, + ]; + ruleFromFileSystem1.rule_id = 'rule-1'; + ruleFromFileSystem1.version = 2; + + const installedRule1 = getRuleMock(getQueryRuleParams()); + installedRule1.params.ruleId = 'rule-1'; + installedRule1.params.version = 1; + installedRule1.params.exceptionsList = []; + + const update = mergeExceptionLists(ruleFromFileSystem1, [installedRule1]); + expect(update.exceptions_list).toEqual(ruleFromFileSystem1.exceptions_list); + }); + + test('should not remove an additional exception_list if an additional one was added by the end user on an immutable rule during an upgrade', () => { + const ruleFromFileSystem1 = getAddPrepackagedRulesSchemaDecodedMock(); + ruleFromFileSystem1.exceptions_list = [ + { + id: 'endpoint_list', + list_id: 'endpoint_list', + namespace_type: 'agnostic', + type: 'endpoint', + }, + ]; + ruleFromFileSystem1.rule_id = 'rule-1'; + ruleFromFileSystem1.version = 2; + + const installedRule1 = getRuleMock(getQueryRuleParams()); + installedRule1.params.ruleId = 'rule-1'; + installedRule1.params.version = 1; + installedRule1.params.exceptionsList = [ + { + id: 'second_exception_list', + list_id: 'some-other-id', + namespace_type: 'single', + type: 'detection', + }, + ]; + + const update = mergeExceptionLists(ruleFromFileSystem1, [installedRule1]); + expect(update.exceptions_list).toEqual([ + ...ruleFromFileSystem1.exceptions_list, + ...installedRule1.params.exceptionsList, + ]); + }); + + test('should not remove an existing exception_list if they are the same between the current installed one and the upgraded one', () => { + const ruleFromFileSystem1 = getAddPrepackagedRulesSchemaDecodedMock(); + ruleFromFileSystem1.exceptions_list = [ + { + id: 'endpoint_list', + list_id: 'endpoint_list', + namespace_type: 'agnostic', + type: 'endpoint', + }, + ]; + ruleFromFileSystem1.rule_id = 'rule-1'; + ruleFromFileSystem1.version = 2; + + const installedRule1 = getRuleMock(getQueryRuleParams()); + installedRule1.params.ruleId = 'rule-1'; + installedRule1.params.version = 1; + installedRule1.params.exceptionsList = [ + { + id: 'endpoint_list', + list_id: 'endpoint_list', + namespace_type: 'agnostic', + type: 'endpoint', + }, + ]; + + const update = mergeExceptionLists(ruleFromFileSystem1, [installedRule1]); + expect(update.exceptions_list).toEqual(ruleFromFileSystem1.exceptions_list); }); - describe('mergeExceptionLists', () => { - test('should add back an exception_list if it was removed by the end user on an immutable rule during an upgrade', () => { - const ruleFromFileSystem1 = getAddPrepackagedRulesSchemaDecodedMock(); - ruleFromFileSystem1.exceptions_list = [ - { - id: 'endpoint_list', - list_id: 'endpoint_list', - namespace_type: 'agnostic', - type: 'endpoint', - }, - ]; - ruleFromFileSystem1.rule_id = 'rule-1'; - ruleFromFileSystem1.version = 2; - - const installedRule1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); - installedRule1.params.ruleId = 'rule-1'; - installedRule1.params.version = 1; - installedRule1.params.exceptionsList = []; - - const update = mergeExceptionLists(ruleFromFileSystem1, [installedRule1]); - expect(update.exceptions_list).toEqual(ruleFromFileSystem1.exceptions_list); - }); - - test('should not remove an additional exception_list if an additional one was added by the end user on an immutable rule during an upgrade', () => { - const ruleFromFileSystem1 = getAddPrepackagedRulesSchemaDecodedMock(); - ruleFromFileSystem1.exceptions_list = [ - { - id: 'endpoint_list', - list_id: 'endpoint_list', - namespace_type: 'agnostic', - type: 'endpoint', - }, - ]; - ruleFromFileSystem1.rule_id = 'rule-1'; - ruleFromFileSystem1.version = 2; - - const installedRule1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); - installedRule1.params.ruleId = 'rule-1'; - installedRule1.params.version = 1; - installedRule1.params.exceptionsList = [ - { - id: 'second_exception_list', - list_id: 'some-other-id', - namespace_type: 'single', - type: 'detection', - }, - ]; - - const update = mergeExceptionLists(ruleFromFileSystem1, [installedRule1]); - expect(update.exceptions_list).toEqual([ - ...ruleFromFileSystem1.exceptions_list, - ...installedRule1.params.exceptionsList, - ]); - }); - - test('should not remove an existing exception_list if they are the same between the current installed one and the upgraded one', () => { - const ruleFromFileSystem1 = getAddPrepackagedRulesSchemaDecodedMock(); - ruleFromFileSystem1.exceptions_list = [ - { - id: 'endpoint_list', - list_id: 'endpoint_list', - namespace_type: 'agnostic', - type: 'endpoint', - }, - ]; - ruleFromFileSystem1.rule_id = 'rule-1'; - ruleFromFileSystem1.version = 2; - - const installedRule1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); - installedRule1.params.ruleId = 'rule-1'; - installedRule1.params.version = 1; - installedRule1.params.exceptionsList = [ - { - id: 'endpoint_list', - list_id: 'endpoint_list', - namespace_type: 'agnostic', - type: 'endpoint', - }, - ]; - - const update = mergeExceptionLists(ruleFromFileSystem1, [installedRule1]); - expect(update.exceptions_list).toEqual(ruleFromFileSystem1.exceptions_list); - }); - - test('should not remove an existing exception_list if the rule has an empty exceptions list', () => { - const ruleFromFileSystem1 = getAddPrepackagedRulesSchemaDecodedMock(); - ruleFromFileSystem1.exceptions_list = []; - ruleFromFileSystem1.rule_id = 'rule-1'; - ruleFromFileSystem1.version = 2; - - const installedRule1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); - installedRule1.params.ruleId = 'rule-1'; - installedRule1.params.version = 1; - installedRule1.params.exceptionsList = [ - { - id: 'endpoint_list', - list_id: 'endpoint_list', - namespace_type: 'agnostic', - type: 'endpoint', - }, - ]; - - const update = mergeExceptionLists(ruleFromFileSystem1, [installedRule1]); - expect(update.exceptions_list).toEqual(installedRule1.params.exceptionsList); - }); + test('should not remove an existing exception_list if the rule has an empty exceptions list', () => { + const ruleFromFileSystem1 = getAddPrepackagedRulesSchemaDecodedMock(); + ruleFromFileSystem1.exceptions_list = []; + ruleFromFileSystem1.rule_id = 'rule-1'; + ruleFromFileSystem1.version = 2; + + const installedRule1 = getRuleMock(getQueryRuleParams()); + installedRule1.params.ruleId = 'rule-1'; + installedRule1.params.version = 1; + installedRule1.params.exceptionsList = [ + { + id: 'endpoint_list', + list_id: 'endpoint_list', + namespace_type: 'agnostic', + type: 'endpoint', + }, + ]; + + const update = mergeExceptionLists(ruleFromFileSystem1, [installedRule1]); + expect(update.exceptions_list).toEqual(installedRule1.params.exceptionsList); }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/install_prepacked_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/install_prepacked_rules.ts index 298a87565bdf..bffa0bc39eb9 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/install_prepacked_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/install_prepacked_rules.ts @@ -14,8 +14,7 @@ import { PartialFilter } from '../types'; export const installPrepackagedRules = ( rulesClient: RulesClient, rules: AddPrepackagedRulesSchemaDecoded[], - outputIndex: string, - isRuleRegistryEnabled: boolean + outputIndex: string ): Array>> => rules.reduce>>>((acc, rule) => { const { @@ -72,7 +71,6 @@ export const installPrepackagedRules = ( return [ ...acc, createRules({ - isRuleRegistryEnabled, rulesClient, anomalyThreshold, author, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.mock.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.mock.ts index b02e297f78c8..1250746a2b3c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.mock.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.mock.ts @@ -5,12 +5,13 @@ * 2.0. */ -import { PatchRulesOptions } from './types'; import { rulesClientMock } from '@kbn/alerting-plugin/server/mocks'; -import { getAlertMock } from '../routes/__mocks__/request_responses'; + +import { PatchRulesOptions } from './types'; +import { getRuleMock } from '../routes/__mocks__/request_responses'; import { getMlRuleParams, getQueryRuleParams } from '../schemas/rule_schemas.mock'; -export const getPatchRulesOptionsMock = (isRuleRegistryEnabled: boolean): PatchRulesOptions => ({ +export const getPatchRulesOptionsMock = (): PatchRulesOptions => ({ author: ['Elastic'], buildingBlockType: undefined, rulesClient: rulesClientMock.create(), @@ -59,10 +60,10 @@ export const getPatchRulesOptionsMock = (isRuleRegistryEnabled: boolean): PatchR version: 1, exceptionsList: [], actions: [], - rule: getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()), + rule: getRuleMock(getQueryRuleParams()), }); -export const getPatchMlRulesOptionsMock = (isRuleRegistryEnabled: boolean): PatchRulesOptions => ({ +export const getPatchMlRulesOptionsMock = (): PatchRulesOptions => ({ author: ['Elastic'], buildingBlockType: undefined, rulesClient: rulesClientMock.create(), @@ -111,5 +112,5 @@ export const getPatchMlRulesOptionsMock = (isRuleRegistryEnabled: boolean): Patc version: 1, exceptionsList: [], actions: [], - rule: getAlertMock(isRuleRegistryEnabled, getMlRuleParams()), + rule: getRuleMock(getMlRuleParams()), }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.test.ts index e16a108dec90..e2e09e130d5c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.test.ts @@ -5,25 +5,23 @@ * 2.0. */ +import { RulesClientMock } from '@kbn/alerting-plugin/server/rules_client.mock'; + import { patchRules } from './patch_rules'; import { getPatchRulesOptionsMock, getPatchMlRulesOptionsMock } from './patch_rules.mock'; import { PatchRulesOptions } from './types'; -import { RulesClientMock } from '@kbn/alerting-plugin/server/rules_client.mock'; -import { getAlertMock } from '../routes/__mocks__/request_responses'; +import { getRuleMock } from '../routes/__mocks__/request_responses'; import { getQueryRuleParams } from '../schemas/rule_schemas.mock'; -describe.each([ - ['Legacy', false], - ['RAC', true], -])('patchRules - %s', (_, isRuleRegistryEnabled) => { +describe('patchRules', () => { it('should call rulesClient.disable if the rule was enabled and enabled is false', async () => { - const rulesOptionsMock = getPatchRulesOptionsMock(isRuleRegistryEnabled); + const rulesOptionsMock = getPatchRulesOptionsMock(); const ruleOptions: PatchRulesOptions = { ...rulesOptionsMock, enabled: false, }; (rulesOptionsMock.rulesClient as unknown as RulesClientMock).update.mockResolvedValue( - getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()) + getRuleMock(getQueryRuleParams()) ); await patchRules(ruleOptions); expect(ruleOptions.rulesClient.disable).toHaveBeenCalledWith( @@ -34,7 +32,7 @@ describe.each([ }); it('should call rulesClient.enable if the rule was disabled and enabled is true', async () => { - const rulesOptionsMock = getPatchRulesOptionsMock(isRuleRegistryEnabled); + const rulesOptionsMock = getPatchRulesOptionsMock(); const ruleOptions: PatchRulesOptions = { ...rulesOptionsMock, enabled: true, @@ -43,7 +41,7 @@ describe.each([ ruleOptions.rule.enabled = false; } (rulesOptionsMock.rulesClient as unknown as RulesClientMock).update.mockResolvedValue( - getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()) + getRuleMock(getQueryRuleParams()) ); await patchRules(ruleOptions); expect(ruleOptions.rulesClient.enable).toHaveBeenCalledWith( @@ -54,7 +52,7 @@ describe.each([ }); it('calls the rulesClient with legacy ML params', async () => { - const rulesOptionsMock = getPatchMlRulesOptionsMock(isRuleRegistryEnabled); + const rulesOptionsMock = getPatchMlRulesOptionsMock(); const ruleOptions: PatchRulesOptions = { ...rulesOptionsMock, enabled: true, @@ -63,7 +61,7 @@ describe.each([ ruleOptions.rule.enabled = false; } (rulesOptionsMock.rulesClient as unknown as RulesClientMock).update.mockResolvedValue( - getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()) + getRuleMock(getQueryRuleParams()) ); await patchRules(ruleOptions); expect(ruleOptions.rulesClient.update).toHaveBeenCalledWith( @@ -79,7 +77,7 @@ describe.each([ }); it('calls the rulesClient with new ML params', async () => { - const rulesOptionsMock = getPatchMlRulesOptionsMock(isRuleRegistryEnabled); + const rulesOptionsMock = getPatchMlRulesOptionsMock(); const ruleOptions: PatchRulesOptions = { ...rulesOptionsMock, machineLearningJobId: ['new_job_1', 'new_job_2'], @@ -89,7 +87,7 @@ describe.each([ ruleOptions.rule.enabled = false; } (rulesOptionsMock.rulesClient as unknown as RulesClientMock).update.mockResolvedValue( - getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()) + getRuleMock(getQueryRuleParams()) ); await patchRules(ruleOptions); expect(ruleOptions.rulesClient.update).toHaveBeenCalledWith( @@ -106,7 +104,7 @@ describe.each([ describe('regression tests', () => { it("updates the rule's actions if provided", async () => { - const rulesOptionsMock = getPatchRulesOptionsMock(isRuleRegistryEnabled); + const rulesOptionsMock = getPatchRulesOptionsMock(); const ruleOptions: PatchRulesOptions = { ...rulesOptionsMock, actions: [ @@ -121,7 +119,7 @@ describe.each([ ], }; (rulesOptionsMock.rulesClient as unknown as RulesClientMock).update.mockResolvedValue( - getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()) + getRuleMock(getQueryRuleParams()) ); await patchRules(ruleOptions); expect(ruleOptions.rulesClient.update).toHaveBeenCalledWith( @@ -143,7 +141,7 @@ describe.each([ }); it('does not update actions if none are specified', async () => { - const ruleOptions = getPatchRulesOptionsMock(isRuleRegistryEnabled); + const ruleOptions = getPatchRulesOptionsMock(); delete ruleOptions.actions; if (ruleOptions.rule != null) { ruleOptions.rule.actions = [ @@ -158,7 +156,7 @@ describe.each([ ]; } (ruleOptions.rulesClient as unknown as RulesClientMock).update.mockResolvedValue( - getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()) + getRuleMock(getQueryRuleParams()) ); await patchRules(ruleOptions); expect(ruleOptions.rulesClient.update).toHaveBeenCalledWith( diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/read_rules.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/read_rules.test.ts index 683744640dc9..dd0d1ff7090c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/read_rules.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/read_rules.test.ts @@ -8,8 +8,8 @@ import { readRules } from './read_rules'; import { rulesClientMock } from '@kbn/alerting-plugin/server/mocks'; import { - resolveAlertMock, - getAlertMock, + resolveRuleMock, + getRuleMock, getFindResultWithSingleHit, } from '../routes/__mocks__/request_responses'; import { getQueryRuleParams } from '../schemas/rule_schemas.mock'; @@ -25,10 +25,7 @@ export class TestError extends Error { public output: { statusCode: number }; } -describe.each([ - ['Legacy', false], - ['RAC', true], -])('read_rules - %s', (_, isRuleRegistryEnabled) => { +describe('read_rules', () => { beforeEach(() => { jest.resetAllMocks(); jest.restoreAllMocks(); @@ -37,27 +34,23 @@ describe.each([ describe('readRules', () => { test('should return the output from rulesClient if id is set but ruleId is undefined', async () => { const rulesClient = rulesClientMock.create(); - rulesClient.resolve.mockResolvedValue( - resolveAlertMock(isRuleRegistryEnabled, getQueryRuleParams()) - ); + rulesClient.resolve.mockResolvedValue(resolveRuleMock(getQueryRuleParams())); const rule = await readRules({ - isRuleRegistryEnabled, rulesClient, id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', ruleId: undefined, }); - expect(rule).toEqual(getAlertMock(isRuleRegistryEnabled, getQueryRuleParams())); + expect(rule).toEqual(getRuleMock(getQueryRuleParams())); }); test('should return null if saved object found by alerts client given id is not alert type', async () => { const rulesClient = rulesClientMock.create(); - const result = resolveAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const result = resolveRuleMock(getQueryRuleParams()); // @ts-expect-error delete result.alertTypeId; rulesClient.resolve.mockResolvedValue(result); const rule = await readRules({ - isRuleRegistryEnabled, rulesClient, id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', ruleId: undefined, @@ -72,7 +65,6 @@ describe.each([ }); const rule = await readRules({ - isRuleRegistryEnabled, rulesClient, id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', ruleId: undefined, @@ -87,7 +79,6 @@ describe.each([ }); try { await readRules({ - isRuleRegistryEnabled, rulesClient, id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', ruleId: undefined, @@ -99,25 +90,23 @@ describe.each([ test('should return the output from rulesClient if id is undefined but ruleId is set', async () => { const rulesClient = rulesClientMock.create(); - rulesClient.get.mockResolvedValue(getAlertMock(isRuleRegistryEnabled, getQueryRuleParams())); - rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled)); + rulesClient.get.mockResolvedValue(getRuleMock(getQueryRuleParams())); + rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); const rule = await readRules({ - isRuleRegistryEnabled, rulesClient, id: undefined, ruleId: 'rule-1', }); - expect(rule).toEqual(getAlertMock(isRuleRegistryEnabled, getQueryRuleParams())); + expect(rule).toEqual(getRuleMock(getQueryRuleParams())); }); test('should return null if the output from rulesClient with ruleId set is empty', async () => { const rulesClient = rulesClientMock.create(); - rulesClient.get.mockResolvedValue(getAlertMock(isRuleRegistryEnabled, getQueryRuleParams())); + rulesClient.get.mockResolvedValue(getRuleMock(getQueryRuleParams())); rulesClient.find.mockResolvedValue({ data: [], page: 0, perPage: 1, total: 0 }); const rule = await readRules({ - isRuleRegistryEnabled, rulesClient, id: undefined, ruleId: 'rule-1', @@ -127,25 +116,23 @@ describe.each([ test('should return the output from rulesClient if id is null but ruleId is set', async () => { const rulesClient = rulesClientMock.create(); - rulesClient.get.mockResolvedValue(getAlertMock(isRuleRegistryEnabled, getQueryRuleParams())); - rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled)); + rulesClient.get.mockResolvedValue(getRuleMock(getQueryRuleParams())); + rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); const rule = await readRules({ - isRuleRegistryEnabled, rulesClient, id: undefined, ruleId: 'rule-1', }); - expect(rule).toEqual(getAlertMock(isRuleRegistryEnabled, getQueryRuleParams())); + expect(rule).toEqual(getRuleMock(getQueryRuleParams())); }); test('should return null if id and ruleId are undefined', async () => { const rulesClient = rulesClientMock.create(); - rulesClient.get.mockResolvedValue(getAlertMock(isRuleRegistryEnabled, getQueryRuleParams())); - rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled)); + rulesClient.get.mockResolvedValue(getRuleMock(getQueryRuleParams())); + rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); const rule = await readRules({ - isRuleRegistryEnabled, rulesClient, id: undefined, ruleId: undefined, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/read_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/read_rules.ts index 48f3ee86bd0e..14a63df87ebe 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/read_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/read_rules.ts @@ -20,7 +20,6 @@ import { isAlertType, ReadRuleOptions } from './types'; * a filter query against the tags using `alert.attributes.tags: "__internal:${ruleId}"]` */ export const readRules = async ({ - isRuleRegistryEnabled, rulesClient, id, ruleId, @@ -30,7 +29,7 @@ export const readRules = async ({ if (id != null) { try { const rule = await rulesClient.resolve({ id }); - if (isAlertType(isRuleRegistryEnabled, rule)) { + if (isAlertType(rule)) { if (rule?.outcome === 'exactMatch') { const { outcome, ...restOfRule } = rule; return restOfRule; @@ -49,7 +48,6 @@ export const readRules = async ({ } } else if (ruleId != null) { const ruleFromFind = await findRules({ - isRuleRegistryEnabled, rulesClient, filter: `alert.attributes.tags: "${INTERNAL_RULE_ID_KEY}:${ruleId}"`, page: 1, @@ -58,10 +56,7 @@ export const readRules = async ({ sortField: undefined, sortOrder: undefined, }); - if ( - ruleFromFind.data.length === 0 || - !isAlertType(isRuleRegistryEnabled, ruleFromFind.data[0]) - ) { + if (ruleFromFind.data.length === 0 || !isAlertType(ruleFromFind.data[0])) { return null; } else { return ruleFromFind.data[0]; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/types.ts index 95def15c20e2..8b560d0edea0 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/types.ts @@ -39,7 +39,7 @@ import type { ThrottleOrNull, } from '@kbn/securitysolution-io-ts-alerting-types'; import type { VersionOrUndefined, Version } from '@kbn/securitysolution-io-ts-types'; -import { SIGNALS_ID, ruleTypeMappings } from '@kbn/securitysolution-rules'; +import { ruleTypeMappings } from '@kbn/securitysolution-rules'; import type { ListArrayOrUndefined, ListArray } from '@kbn/securitysolution-io-ts-list-types'; import { RulesClient, PartialRule } from '@kbn/alerting-plugin/server'; @@ -125,20 +125,16 @@ export interface Clients { } export const isAlertTypes = ( - isRuleRegistryEnabled: boolean, partialAlert: Array> ): partialAlert is RuleAlertType[] => { - return partialAlert.every((rule) => isAlertType(isRuleRegistryEnabled, rule)); + return partialAlert.every((rule) => isAlertType(rule)); }; export const isAlertType = ( - isRuleRegistryEnabled: boolean, partialAlert: PartialRule ): partialAlert is RuleAlertType => { const ruleTypeValues = Object.values(ruleTypeMappings) as unknown as string[]; - return isRuleRegistryEnabled - ? ruleTypeValues.includes(partialAlert.alertTypeId as string) - : partialAlert.alertTypeId === SIGNALS_ID; + return ruleTypeValues.includes(partialAlert.alertTypeId as string); }; export interface CreateRulesOptions { @@ -192,7 +188,6 @@ export interface CreateRulesOptions { version: Version; exceptionsList: ListArray; actions: RuleAlertAction[]; - isRuleRegistryEnabled: boolean; namespace?: NamespaceOrUndefined; id?: string; } @@ -261,7 +256,6 @@ interface PatchRulesFieldsOptions { } export interface ReadRuleOptions { - isRuleRegistryEnabled: boolean; rulesClient: RulesClient; id: IdOrUndefined; ruleId: RuleIdOrUndefined; @@ -274,7 +268,6 @@ export interface DeleteRuleOptions { } export interface FindRuleOptions { - isRuleRegistryEnabled: boolean; rulesClient: RulesClient; perPage: PerPageOrUndefined; page: PageOrUndefined; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_prepacked_rules.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_prepacked_rules.test.ts index ee6179a9c7e6..96fba6703a53 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_prepacked_rules.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_prepacked_rules.test.ts @@ -15,10 +15,7 @@ import { ruleExecutionLogMock } from '../rule_execution_log/__mocks__'; jest.mock('./patch_rules'); -describe.each([ - ['Legacy', false], - ['RAC', true], -])('updatePrepackagedRules - %s', (_, isRuleRegistryEnabled) => { +describe('updatePrepackagedRules', () => { let rulesClient: ReturnType; let savedObjectsClient: ReturnType; let ruleExecutionLog: ReturnType; @@ -40,14 +37,13 @@ describe.each([ ]; const outputIndex = 'outputIndex'; const prepackagedRule = getAddPrepackagedRulesSchemaDecodedMock(); - rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled)); + rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); await updatePrepackagedRules( rulesClient, savedObjectsClient, [{ ...prepackagedRule, actions }], outputIndex, - isRuleRegistryEnabled, ruleExecutionLog ); @@ -71,14 +67,13 @@ describe.each([ threat_query: 'threat:*', }; const prepackagedRule = getAddPrepackagedRulesSchemaDecodedMock(); - rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled)); + rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); await updatePrepackagedRules( rulesClient, savedObjectsClient, [{ ...prepackagedRule, ...updatedThreatParams }], 'output-index', - isRuleRegistryEnabled, ruleExecutionLog ); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_prepacked_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_prepacked_rules.ts index b8ff87dbd0ff..ad35e11d3566 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_prepacked_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_prepacked_rules.ts @@ -35,7 +35,6 @@ export const updatePrepackagedRules = async ( savedObjectsClient: SavedObjectsClientContract, rules: AddPrepackagedRulesSchemaDecoded[], outputIndex: string, - isRuleRegistryEnabled: boolean, ruleExecutionLog: IRuleExecutionLogForRoutes ): Promise => { const ruleChunks = chunk(MAX_RULES_TO_UPDATE_IN_PARALLEL, rules); @@ -45,7 +44,6 @@ export const updatePrepackagedRules = async ( savedObjectsClient, ruleChunk, outputIndex, - isRuleRegistryEnabled, ruleExecutionLog ); await Promise.all(rulePromises); @@ -65,7 +63,6 @@ export const createPromises = ( savedObjectsClient: SavedObjectsClientContract, rules: AddPrepackagedRulesSchemaDecoded[], outputIndex: string, - isRuleRegistryEnabled: boolean, ruleExecutionLog: IRuleExecutionLogForRoutes ): Array | null>> => { return rules.map(async (rule) => { @@ -118,7 +115,6 @@ export const createPromises = ( } = rule; const existingRule = await readRules({ - isRuleRegistryEnabled, rulesClient, ruleId, id: undefined, @@ -149,7 +145,6 @@ export const createPromises = ( return (await createRules({ id: migratedRule.id, - isRuleRegistryEnabled, rulesClient, anomalyThreshold, author, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_rules.mock.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_rules.mock.ts index 9482a1d3aa1c..9f19e68443c4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_rules.mock.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_rules.mock.ts @@ -11,27 +11,25 @@ import { getUpdateMachineLearningSchemaMock, getUpdateRulesSchemaMock, } from '../../../../common/detection_engine/schemas/request/rule_schemas.mock'; -import { getAlertMock } from '../routes/__mocks__/request_responses'; +import { getRuleMock } from '../routes/__mocks__/request_responses'; import { getQueryRuleParams } from '../schemas/rule_schemas.mock'; -export const getUpdateRulesOptionsMock = (isRuleRegistryEnabled: boolean) => ({ +export const getUpdateRulesOptionsMock = () => ({ spaceId: 'default', rulesClient: rulesClientMock.create(), savedObjectsClient: savedObjectsClientMock.create(), defaultOutputIndex: '.siem-signals-default', - existingRule: getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()), - migratedRule: getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()), + existingRule: getRuleMock(getQueryRuleParams()), + migratedRule: getRuleMock(getQueryRuleParams()), ruleUpdate: getUpdateRulesSchemaMock(), - isRuleRegistryEnabled, }); -export const getUpdateMlRulesOptionsMock = (isRuleRegistryEnabled: boolean) => ({ +export const getUpdateMlRulesOptionsMock = () => ({ spaceId: 'default', rulesClient: rulesClientMock.create(), savedObjectsClient: savedObjectsClientMock.create(), defaultOutputIndex: '.siem-signals-default', - existingRule: getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()), - migratedRule: getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()), + existingRule: getRuleMock(getQueryRuleParams()), + migratedRule: getRuleMock(getQueryRuleParams()), ruleUpdate: getUpdateMachineLearningSchemaMock(), - isRuleRegistryEnabled, }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_rules.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_rules.test.ts index 519ecd0c01ed..a382fc27ed56 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_rules.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_rules.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { getAlertMock, resolveAlertMock } from '../routes/__mocks__/request_responses'; +import { getRuleMock, resolveRuleMock } from '../routes/__mocks__/request_responses'; import { updateRules } from './update_rules'; import { getUpdateRulesOptionsMock, getUpdateMlRulesOptionsMock } from './update_rules.mock'; import { RulesClientMock } from '@kbn/alerting-plugin/server/rules_client.mock'; @@ -14,10 +14,10 @@ import { getMlRuleParams, getQueryRuleParams } from '../schemas/rule_schemas.moc // Failing with rule registry enabled describe('updateRules', () => { it('should call rulesClient.disable if the rule was enabled and enabled is false', async () => { - const rulesOptionsMock = getUpdateRulesOptionsMock(true); + const rulesOptionsMock = getUpdateRulesOptionsMock(); rulesOptionsMock.ruleUpdate.enabled = false; (rulesOptionsMock.rulesClient as unknown as RulesClientMock).update.mockResolvedValue( - getAlertMock(true, getQueryRuleParams()) + getRuleMock(getQueryRuleParams()) ); await updateRules(rulesOptionsMock); @@ -30,7 +30,7 @@ describe('updateRules', () => { }); it('should call rulesClient.enable if the rule was disabled and enabled is true', async () => { - const baseRulesOptionsMock = getUpdateRulesOptionsMock(true); + const baseRulesOptionsMock = getUpdateRulesOptionsMock(); const rulesOptionsMock = { ...baseRulesOptionsMock, existingRule: { @@ -41,7 +41,7 @@ describe('updateRules', () => { rulesOptionsMock.ruleUpdate.enabled = true; (rulesOptionsMock.rulesClient as unknown as RulesClientMock).update.mockResolvedValue( - getAlertMock(true, getQueryRuleParams()) + getRuleMock(getQueryRuleParams()) ); await updateRules(rulesOptionsMock); @@ -54,15 +54,15 @@ describe('updateRules', () => { }); it('calls the rulesClient with params', async () => { - const rulesOptionsMock = getUpdateMlRulesOptionsMock(true); + const rulesOptionsMock = getUpdateMlRulesOptionsMock(); rulesOptionsMock.ruleUpdate.enabled = true; (rulesOptionsMock.rulesClient as unknown as RulesClientMock).update.mockResolvedValue( - getAlertMock(true, getMlRuleParams()) + getRuleMock(getMlRuleParams()) ); (rulesOptionsMock.rulesClient as unknown as RulesClientMock).resolve.mockResolvedValue( - resolveAlertMock(true, getMlRuleParams()) + resolveRuleMock(getMlRuleParams()) ); await updateRules(rulesOptionsMock); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/schemas/rule_converters.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/schemas/rule_converters.ts index b806b42e5180..8b7fdcf56853 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/schemas/rule_converters.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/schemas/rule_converters.ts @@ -7,7 +7,7 @@ import uuid from 'uuid'; -import { SIGNALS_ID, ruleTypeMappings } from '@kbn/securitysolution-rules'; +import { ruleTypeMappings } from '@kbn/securitysolution-rules'; import { ResolvedSanitizedRule, SanitizedRule } from '@kbn/alerting-plugin/common'; import { @@ -127,15 +127,14 @@ export const typeSpecificSnakeToCamel = (params: CreateTypeSpecific): TypeSpecif export const convertCreateAPIToInternalSchema = ( input: CreateRulesSchema, - siemClient: AppClient, - isRuleRegistryEnabled: boolean + siemClient: AppClient ): InternalRuleCreate => { const typeSpecificParams = typeSpecificSnakeToCamel(input); const newRuleId = input.rule_id ?? uuid.v4(); return { name: input.name, tags: addTags(input.tags ?? [], newRuleId, false), - alertTypeId: isRuleRegistryEnabled ? ruleTypeMappings[input.type] : SIGNALS_ID, + alertTypeId: ruleTypeMappings[input.type], consumer: SERVER_APP_ID, params: { author: input.author ?? [], diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/eql.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/eql.ts index e601f41aa762..33ee15d683b7 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/eql.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/eql.ts @@ -15,9 +15,6 @@ import { } from '@kbn/alerting-plugin/server'; import { buildEqlSearchRequest } from '../build_events_query'; import { hasLargeValueItem } from '../../../../../common/detection_engine/utils'; -import { isOutdated } from '../../migrations/helpers'; -import { getIndexVersion } from '../../routes/index/get_index_version'; -import { MIN_EQL_RULE_INDEX_VERSION } from '../../routes/index/get_signals_template'; import { getInputIndex } from '../get_input_output_index'; import { @@ -71,27 +68,7 @@ export const eqlExecutor = async ({ ); result.warning = true; } - if (!experimentalFeatures.ruleRegistryEnabled) { - try { - const signalIndexVersion = await getIndexVersion( - services.scopedClusterClient.asCurrentUser, - ruleParams.outputIndex - ); - if (isOutdated({ current: signalIndexVersion, target: MIN_EQL_RULE_INDEX_VERSION })) { - throw new Error( - `EQL based rules require an update to version ${MIN_EQL_RULE_INDEX_VERSION} of the detection alerts index mapping` - ); - } - } catch (err) { - if (err.statusCode === 403) { - throw new Error( - `EQL based rules require the user that created it to have the view_index_metadata, read, and write permissions for index: ${ruleParams.outputIndex}` - ); - } else { - throw err; - } - } - } + const inputIndex = await getInputIndex({ experimentalFeatures, services, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/tags/read_tags.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/tags/read_tags.test.ts index 3886a7dacc3b..4d6327e05f2d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/tags/read_tags.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/tags/read_tags.test.ts @@ -6,27 +6,25 @@ */ import { rulesClientMock } from '@kbn/alerting-plugin/server/mocks'; -import { getAlertMock, getFindResultWithMultiHits } from '../routes/__mocks__/request_responses'; + +import { getRuleMock, getFindResultWithMultiHits } from '../routes/__mocks__/request_responses'; import { INTERNAL_RULE_ID_KEY, INTERNAL_IDENTIFIER } from '../../../../common/constants'; import { readRawTags, readTags, convertTagsToSet, convertToTags, isTags } from './read_tags'; import { getQueryRuleParams } from '../schemas/rule_schemas.mock'; -describe.each([ - ['Legacy', false], - ['RAC', true], -])('read_tags - %s', (_, isRuleRegistryEnabled) => { +describe('read_tags', () => { afterEach(() => { jest.resetAllMocks(); }); describe('readRawTags', () => { test('it should return the intersection of tags to where none are repeating', async () => { - const result1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const result1 = getRuleMock(getQueryRuleParams()); result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d'; result1.params.ruleId = 'rule-1'; result1.tags = ['tag 1', 'tag 2', 'tag 3']; - const result2 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const result2 = getRuleMock(getQueryRuleParams()); result2.id = '5baa53f8-96da-44ee-ad58-41bccb7f9f3d'; result2.params.ruleId = 'rule-2'; result2.tags = ['tag 1', 'tag 2', 'tag 3', 'tag 4']; @@ -34,17 +32,17 @@ describe.each([ const rulesClient = rulesClientMock.create(); rulesClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1, result2] })); - const tags = await readRawTags({ isRuleRegistryEnabled, rulesClient }); + const tags = await readRawTags({ rulesClient }); expect(tags).toEqual(['tag 1', 'tag 2', 'tag 3', 'tag 4']); }); test('it should return the intersection of tags to where some are repeating values', async () => { - const result1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const result1 = getRuleMock(getQueryRuleParams()); result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d'; result1.params.ruleId = 'rule-1'; result1.tags = ['tag 1', 'tag 2', 'tag 2', 'tag 3']; - const result2 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const result2 = getRuleMock(getQueryRuleParams()); result2.id = '5baa53f8-96da-44ee-ad58-41bccb7f9f3d'; result2.params.ruleId = 'rule-2'; result2.tags = ['tag 1', 'tag 2', 'tag 2', 'tag 3', 'tag 4']; @@ -52,17 +50,17 @@ describe.each([ const rulesClient = rulesClientMock.create(); rulesClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1, result2] })); - const tags = await readRawTags({ isRuleRegistryEnabled, rulesClient }); + const tags = await readRawTags({ rulesClient }); expect(tags).toEqual(['tag 1', 'tag 2', 'tag 3', 'tag 4']); }); test('it should work with no tags defined between two results', async () => { - const result1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const result1 = getRuleMock(getQueryRuleParams()); result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d'; result1.params.ruleId = 'rule-1'; result1.tags = []; - const result2 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const result2 = getRuleMock(getQueryRuleParams()); result2.id = '5baa53f8-96da-44ee-ad58-41bccb7f9f3d'; result2.params.ruleId = 'rule-2'; result2.tags = []; @@ -70,12 +68,12 @@ describe.each([ const rulesClient = rulesClientMock.create(); rulesClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1, result2] })); - const tags = await readRawTags({ isRuleRegistryEnabled, rulesClient }); + const tags = await readRawTags({ rulesClient }); expect(tags).toEqual([]); }); test('it should work with a single tag which has repeating values in it', async () => { - const result1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const result1 = getRuleMock(getQueryRuleParams()); result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d'; result1.params.ruleId = 'rule-1'; result1.tags = ['tag 1', 'tag 1', 'tag 1', 'tag 2']; @@ -83,12 +81,12 @@ describe.each([ const rulesClient = rulesClientMock.create(); rulesClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1] })); - const tags = await readRawTags({ isRuleRegistryEnabled, rulesClient }); + const tags = await readRawTags({ rulesClient }); expect(tags).toEqual(['tag 1', 'tag 2']); }); test('it should work with a single tag which has empty tags', async () => { - const result1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const result1 = getRuleMock(getQueryRuleParams()); result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d'; result1.params.ruleId = 'rule-1'; result1.tags = []; @@ -96,19 +94,19 @@ describe.each([ const rulesClient = rulesClientMock.create(); rulesClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1] })); - const tags = await readRawTags({ isRuleRegistryEnabled, rulesClient }); + const tags = await readRawTags({ rulesClient }); expect(tags).toEqual([]); }); }); describe('readTags', () => { test('it should return the intersection of tags to where none are repeating', async () => { - const result1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const result1 = getRuleMock(getQueryRuleParams()); result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d'; result1.params.ruleId = 'rule-1'; result1.tags = ['tag 1', 'tag 2', 'tag 3']; - const result2 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const result2 = getRuleMock(getQueryRuleParams()); result2.id = '5baa53f8-96da-44ee-ad58-41bccb7f9f3d'; result2.params.ruleId = 'rule-2'; result2.tags = ['tag 1', 'tag 2', 'tag 3', 'tag 4']; @@ -116,17 +114,17 @@ describe.each([ const rulesClient = rulesClientMock.create(); rulesClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1, result2] })); - const tags = await readTags({ isRuleRegistryEnabled, rulesClient }); + const tags = await readTags({ rulesClient }); expect(tags).toEqual(['tag 1', 'tag 2', 'tag 3', 'tag 4']); }); test('it should return the intersection of tags to where some are repeating values', async () => { - const result1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const result1 = getRuleMock(getQueryRuleParams()); result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d'; result1.params.ruleId = 'rule-1'; result1.tags = ['tag 1', 'tag 2', 'tag 2', 'tag 3']; - const result2 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const result2 = getRuleMock(getQueryRuleParams()); result2.id = '5baa53f8-96da-44ee-ad58-41bccb7f9f3d'; result2.params.ruleId = 'rule-2'; result2.tags = ['tag 1', 'tag 2', 'tag 2', 'tag 3', 'tag 4']; @@ -134,17 +132,17 @@ describe.each([ const rulesClient = rulesClientMock.create(); rulesClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1, result2] })); - const tags = await readTags({ isRuleRegistryEnabled, rulesClient }); + const tags = await readTags({ rulesClient }); expect(tags).toEqual(['tag 1', 'tag 2', 'tag 3', 'tag 4']); }); test('it should work with no tags defined between two results', async () => { - const result1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const result1 = getRuleMock(getQueryRuleParams()); result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d'; result1.params.ruleId = 'rule-1'; result1.tags = []; - const result2 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const result2 = getRuleMock(getQueryRuleParams()); result2.id = '5baa53f8-96da-44ee-ad58-41bccb7f9f3d'; result2.params.ruleId = 'rule-2'; result2.tags = []; @@ -152,12 +150,12 @@ describe.each([ const rulesClient = rulesClientMock.create(); rulesClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1, result2] })); - const tags = await readTags({ isRuleRegistryEnabled, rulesClient }); + const tags = await readTags({ rulesClient }); expect(tags).toEqual([]); }); test('it should work with a single tag which has repeating values in it', async () => { - const result1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const result1 = getRuleMock(getQueryRuleParams()); result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d'; result1.params.ruleId = 'rule-1'; result1.tags = ['tag 1', 'tag 1', 'tag 1', 'tag 2']; @@ -165,12 +163,12 @@ describe.each([ const rulesClient = rulesClientMock.create(); rulesClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1] })); - const tags = await readTags({ isRuleRegistryEnabled, rulesClient }); + const tags = await readTags({ rulesClient }); expect(tags).toEqual(['tag 1', 'tag 2']); }); test('it should work with a single tag which has empty tags', async () => { - const result1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const result1 = getRuleMock(getQueryRuleParams()); result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d'; result1.params.ruleId = 'rule-1'; result1.tags = []; @@ -178,12 +176,12 @@ describe.each([ const rulesClient = rulesClientMock.create(); rulesClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1] })); - const tags = await readTags({ isRuleRegistryEnabled, rulesClient }); + const tags = await readTags({ rulesClient }); expect(tags).toEqual([]); }); test('it should filter out any __internal tags for things such as alert_id', async () => { - const result1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const result1 = getRuleMock(getQueryRuleParams()); result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d'; result1.params.ruleId = 'rule-1'; result1.tags = [ @@ -195,12 +193,12 @@ describe.each([ const rulesClient = rulesClientMock.create(); rulesClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1] })); - const tags = await readTags({ isRuleRegistryEnabled, rulesClient }); + const tags = await readTags({ rulesClient }); expect(tags).toEqual(['tag 1']); }); test('it should filter out any __internal tags with two different results', async () => { - const result1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const result1 = getRuleMock(getQueryRuleParams()); result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d'; result1.params.ruleId = 'rule-1'; result1.tags = [ @@ -213,7 +211,7 @@ describe.each([ 'tag 5', ]; - const result2 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const result2 = getRuleMock(getQueryRuleParams()); result2.id = '5baa53f8-96da-44ee-ad58-41bccb7f9f3d'; result2.params.ruleId = 'rule-2'; result2.tags = [ @@ -228,19 +226,19 @@ describe.each([ const rulesClient = rulesClientMock.create(); rulesClient.find.mockResolvedValue(getFindResultWithMultiHits({ data: [result1] })); - const tags = await readTags({ isRuleRegistryEnabled, rulesClient }); + const tags = await readTags({ rulesClient }); expect(tags).toEqual(['tag 1', 'tag 2', 'tag 3', 'tag 4', 'tag 5']); }); }); describe('convertTagsToSet', () => { test('it should convert the intersection of two tag systems without duplicates', () => { - const result1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const result1 = getRuleMock(getQueryRuleParams()); result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d'; result1.params.ruleId = 'rule-1'; result1.tags = ['tag 1', 'tag 2', 'tag 2', 'tag 3']; - const result2 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const result2 = getRuleMock(getQueryRuleParams()); result2.id = '5baa53f8-96da-44ee-ad58-41bccb7f9f3d'; result2.params.ruleId = 'rule-2'; result2.tags = ['tag 1', 'tag 2', 'tag 2', 'tag 3', 'tag 4']; @@ -258,12 +256,12 @@ describe.each([ describe('convertToTags', () => { test('it should convert the two tag systems together with duplicates', () => { - const result1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const result1 = getRuleMock(getQueryRuleParams()); result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d'; result1.params.ruleId = 'rule-1'; result1.tags = ['tag 1', 'tag 2', 'tag 2', 'tag 3']; - const result2 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const result2 = getRuleMock(getQueryRuleParams()); result2.id = '5baa53f8-96da-44ee-ad58-41bccb7f9f3d'; result2.params.ruleId = 'rule-2'; result2.tags = ['tag 1', 'tag 2', 'tag 2', 'tag 3', 'tag 4']; @@ -284,18 +282,18 @@ describe.each([ }); test('it should filter out anything that is not a tag', () => { - const result1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const result1 = getRuleMock(getQueryRuleParams()); result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d'; result1.params.ruleId = 'rule-1'; result1.tags = ['tag 1', 'tag 2', 'tag 2', 'tag 3']; - const result2 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const result2 = getRuleMock(getQueryRuleParams()); result2.id = '99979e67-19a7-455f-b452-8eded6135716'; result2.params.ruleId = 'rule-2'; // @ts-expect-error delete result2.tags; - const result3 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + const result3 = getRuleMock(getQueryRuleParams()); result3.id = '5baa53f8-96da-44ee-ad58-41bccb7f9f3d'; result3.params.ruleId = 'rule-2'; result3.tags = ['tag 1', 'tag 2', 'tag 2', 'tag 3', 'tag 4']; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/tags/read_tags.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/tags/read_tags.ts index da579ee30654..8f3e1468278c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/tags/read_tags.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/tags/read_tags.ts @@ -40,27 +40,22 @@ export const convertTagsToSet = (tagObjects: object[]): Set => { // then this should be replaced with a an aggregation call. // Ref: https://www.elastic.co/guide/en/kibana/master/saved-objects-api.html export const readTags = async ({ - isRuleRegistryEnabled, rulesClient, }: { - isRuleRegistryEnabled: boolean; rulesClient: RulesClient; }): Promise => { - const tags = await readRawTags({ isRuleRegistryEnabled, rulesClient }); + const tags = await readRawTags({ rulesClient }); return tags.filter((tag) => !tag.startsWith(INTERNAL_IDENTIFIER)); }; export const readRawTags = async ({ - isRuleRegistryEnabled, rulesClient, }: { - isRuleRegistryEnabled: boolean; rulesClient: RulesClient; perPage?: number; }): Promise => { // Get just one record so we can get the total count const firstTags = await findRules({ - isRuleRegistryEnabled, rulesClient, fields: ['tags'], perPage: 1, @@ -71,7 +66,6 @@ export const readRawTags = async ({ }); // Get all the rules to aggregate over all the tags of the rules const rules = await findRules({ - isRuleRegistryEnabled, rulesClient, fields: ['tags'], perPage: firstTags.total, diff --git a/x-pack/plugins/security_solution/server/lib/sourcerer/routes/index.test.ts b/x-pack/plugins/security_solution/server/lib/sourcerer/routes/index.test.ts index 08523d2e0334..232aba9d1ae3 100644 --- a/x-pack/plugins/security_solution/server/lib/sourcerer/routes/index.test.ts +++ b/x-pack/plugins/security_solution/server/lib/sourcerer/routes/index.test.ts @@ -137,7 +137,10 @@ describe('sourcerer route', () => { }); test('returns sourcerer formatted Data Views when SIEM Data View does NOT exist', async () => { createSourcererDataViewRoute(server.router, getStartServicesNotSiem); - const response = await server.inject(getSourcererRequest(mockPatternList), context); + const response = await server.inject( + getSourcererRequest(mockPatternList), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(200); expect(response.body).toEqual(mockDataViewsTransformed); }); @@ -163,7 +166,10 @@ describe('sourcerer route', () => { }, ] as unknown) as StartServicesAccessor; createSourcererDataViewRoute(server.router, getStartServicesSpecial); - const response = await server.inject(getSourcererRequest(mockPatternList), context); + const response = await server.inject( + getSourcererRequest(mockPatternList), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(200); expect(response.body).toEqual(mockDataViewsTransformed); }); @@ -192,14 +198,20 @@ describe('sourcerer route', () => { }, ] as unknown) as StartServicesAccessor; createSourcererDataViewRoute(server.router, getStartServicesSpecial); - await server.inject(getSourcererRequest(mockPatternList), context); + await server.inject( + getSourcererRequest(mockPatternList), + requestContextMock.convertContext(context) + ); expect(mockCreateAndSave).toHaveBeenCalled(); expect(mockCreateAndSave.mock.calls[0][1]).toEqual(true); }); test('returns sourcerer formatted Data Views when SIEM Data View exists', async () => { createSourcererDataViewRoute(server.router, getStartServices); - const response = await server.inject(getSourcererRequest(mockPatternList), context); + const response = await server.inject( + getSourcererRequest(mockPatternList), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(200); expect(response.body).toEqual(mockDataViewsTransformed); }); @@ -207,7 +219,10 @@ describe('sourcerer route', () => { test('returns sourcerer formatted Data Views when SIEM Data View exists and patternList input is changed', async () => { createSourcererDataViewRoute(server.router, getStartServices); mockPatternList.shift(); - const response = await server.inject(getSourcererRequest(mockPatternList), context); + const response = await server.inject( + getSourcererRequest(mockPatternList), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(200); expect(response.body).toEqual({ defaultDataView: { diff --git a/x-pack/plugins/security_solution/server/lib/sourcerer/routes/index.ts b/x-pack/plugins/security_solution/server/lib/sourcerer/routes/index.ts index 4ce0f7be2095..bf69ed03fb79 100644 --- a/x-pack/plugins/security_solution/server/lib/sourcerer/routes/index.ts +++ b/x-pack/plugins/security_solution/server/lib/sourcerer/routes/index.ts @@ -34,7 +34,8 @@ export const createSourcererDataViewRoute = ( }, async (context, request, response) => { const siemResponse = buildSiemResponse(response); - const siemClient = context.securitySolution?.getAppClient(); + const coreContext = await context.core; + const siemClient = (await context.securitySolution)?.getAppClient(); const dataViewId = siemClient.getSourcererDataViewId(); try { @@ -46,8 +47,8 @@ export const createSourcererDataViewRoute = ( ] = await getStartServices(); const dataViewService = await indexPatterns.dataViewsServiceFactory( - context.core.savedObjects.client, - context.core.elasticsearch.client.asCurrentUser, + coreContext.savedObjects.client, + coreContext.elasticsearch.client.asCurrentUser, request, true ); @@ -103,7 +104,7 @@ export const createSourcererDataViewRoute = ( const defaultDataView = await buildSourcererDataView( siemDataView, - context.core.elasticsearch.client.asCurrentUser + coreContext.elasticsearch.client.asCurrentUser ); return response.ok({ body: { @@ -143,6 +144,7 @@ export const getSourcererDataViewRoute = ( }, async (context, request, response) => { const siemResponse = buildSiemResponse(response); + const coreContext = await context.core; const { dataViewId } = request.query; try { const [ @@ -153,8 +155,8 @@ export const getSourcererDataViewRoute = ( ] = await getStartServices(); const dataViewService = await indexPatterns.dataViewsServiceFactory( - context.core.savedObjects.client, - context.core.elasticsearch.client.asCurrentUser, + coreContext.savedObjects.client, + coreContext.elasticsearch.client.asCurrentUser, request, true ); @@ -163,7 +165,7 @@ export const getSourcererDataViewRoute = ( const kibanaDataView = siemDataView ? await buildSourcererDataView( siemDataView, - context.core.elasticsearch.client.asCurrentUser + coreContext.elasticsearch.client.asCurrentUser ) : {}; diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/draft_timelines/clean_draft_timelines/index.test.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/draft_timelines/clean_draft_timelines/index.test.ts index 3d66a6507a67..1a62b7604bfd 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/draft_timelines/clean_draft_timelines/index.test.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/draft_timelines/clean_draft_timelines/index.test.ts @@ -82,7 +82,10 @@ describe('clean draft timelines', () => { timeline: [], }); - const response = await server.inject(cleanDraftTimelinesRequest(TimelineType.default), context); + const response = await server.inject( + cleanDraftTimelinesRequest(TimelineType.default), + requestContextMock.convertContext(context) + ); const req = cleanDraftTimelinesRequest(TimelineType.default); expect(mockPersistTimeline).toHaveBeenCalled(); expect(mockPersistTimeline.mock.calls[0][3]).toEqual({ @@ -106,7 +109,10 @@ describe('clean draft timelines', () => { mockResetTimeline.mockResolvedValue({}); mockGetTimeline.mockResolvedValue({ ...mockGetDraftTimelineValue }); - const response = await server.inject(cleanDraftTimelinesRequest(TimelineType.default), context); + const response = await server.inject( + cleanDraftTimelinesRequest(TimelineType.default), + requestContextMock.convertContext(context) + ); const req = cleanDraftTimelinesRequest(TimelineType.default); expect(mockPersistTimeline).not.toHaveBeenCalled(); diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/draft_timelines/get_draft_timelines/index.test.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/draft_timelines/get_draft_timelines/index.test.ts index 1911f964af4b..cb488ef7d84a 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/draft_timelines/get_draft_timelines/index.test.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/draft_timelines/get_draft_timelines/index.test.ts @@ -83,7 +83,7 @@ describe('get draft timelines', () => { timeline: [], }); const req = getDraftTimelinesRequest(TimelineType.default); - const response = await server.inject(req, context); + const response = await server.inject(req, requestContextMock.convertContext(context)); expect(mockPersistTimeline).toHaveBeenCalled(); expect(mockPersistTimeline.mock.calls[0][3]).toEqual({ ...draftTimelineDefaults, @@ -107,7 +107,7 @@ describe('get draft timelines', () => { const response = await server.inject( getDraftTimelinesRequest(TimelineType.default), - context + requestContextMock.convertContext(context) ); expect(mockPersistTimeline).not.toHaveBeenCalled(); expect(response.status).toEqual(200); diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/prepackaged_timelines/install_prepackaged_timelines/helpers.test.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/prepackaged_timelines/install_prepackaged_timelines/helpers.test.ts index 63e8d9afea9d..4f0690cc288e 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/prepackaged_timelines/install_prepackaged_timelines/helpers.test.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/prepackaged_timelines/install_prepackaged_timelines/helpers.test.ts @@ -26,10 +26,7 @@ import { ImportTimelineResultSchema } from '../../../../../../common/types/timel jest.mock('../../timelines/import_timelines/helpers'); -describe.each([ - ['Legacy', false], - ['RAC', true], -])('installPrepackagedTimelines - %s', (_, isRuleRegistryEnabled) => { +describe('installPrepackagedTimelines', () => { let securitySetup: SecurityPluginSetup; let frameworkRequest: FrameworkRequest; const spyInstallPrepackagedTimelines = jest.spyOn(helpers, 'installPrepackagedTimelines'); @@ -47,7 +44,7 @@ describe.each([ authz: {}, } as unknown as SecurityPluginSetup; - clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled)); + clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); jest.doMock('./helpers', () => { return { @@ -57,7 +54,11 @@ describe.each([ }); const request = addPrepackagedRulesRequest(); - frameworkRequest = await buildFrameworkRequest(context, securitySetup, request); + frameworkRequest = await buildFrameworkRequest( + requestContextMock.convertContext(context), + securitySetup, + request + ); }); afterEach(() => { diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/prepackaged_timelines/install_prepackaged_timelines/index.test.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/prepackaged_timelines/install_prepackaged_timelines/index.test.ts index 55f4a06326fe..34a26b977e38 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/prepackaged_timelines/install_prepackaged_timelines/index.test.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/prepackaged_timelines/install_prepackaged_timelines/index.test.ts @@ -64,7 +64,10 @@ describe('installPrepackagedTimelines', () => { mockCheckTimelinesStatusBeforeInstallResult ); - await server.inject(installPrepackedTimelinesRequest(), context); + await server.inject( + installPrepackedTimelinesRequest(), + requestContextMock.convertContext(context) + ); expect(installPrepackagedTimelines).toHaveBeenCalled(); }); @@ -81,7 +84,10 @@ describe('installPrepackagedTimelines', () => { timelines_updated: 0, }); - const result = await server.inject(installPrepackedTimelinesRequest(), context); + const result = await server.inject( + installPrepackedTimelinesRequest(), + requestContextMock.convertContext(context) + ); expect(result.body).toEqual({ errors: [], @@ -95,7 +101,10 @@ describe('installPrepackagedTimelines', () => { test('should not call installPrepackagedTimelines if it has nothing to install or update', async () => { (checkTimelinesStatus as jest.Mock).mockReturnValue(mockCheckTimelinesStatusAfterInstallResult); - await server.inject(installPrepackedTimelinesRequest(), context); + await server.inject( + installPrepackedTimelinesRequest(), + requestContextMock.convertContext(context) + ); expect(installPrepackagedTimelines).not.toHaveBeenCalled(); }); @@ -103,7 +112,10 @@ describe('installPrepackagedTimelines', () => { test('should return success if it has nothing to install or update', async () => { (checkTimelinesStatus as jest.Mock).mockReturnValue(mockCheckTimelinesStatusAfterInstallResult); - const result = await server.inject(installPrepackedTimelinesRequest(), context); + const result = await server.inject( + installPrepackedTimelinesRequest(), + requestContextMock.convertContext(context) + ); expect(result.body).toEqual({ errors: [], diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/create_timelines/helpers.test.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/create_timelines/helpers.test.ts index 0270d525ae77..be97cbd01e42 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/create_timelines/helpers.test.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/create_timelines/helpers.test.ts @@ -69,7 +69,11 @@ describe('createTimelines', () => { const { context } = requestContextMock.createTools(); const mockRequest = getCreateTimelinesRequest(createTimelineWithoutTimelineId); - frameworkRequest = await buildFrameworkRequest(context, securitySetup, mockRequest); + frameworkRequest = await buildFrameworkRequest( + requestContextMock.convertContext(context), + securitySetup, + mockRequest + ); Date.now = jest.fn().mockReturnValue(new Date('2020-11-04T11:37:31.655Z')); }); diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/create_timelines/index.test.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/create_timelines/index.test.ts index 5e066e419c4b..9de715751ca7 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/create_timelines/index.test.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/create_timelines/index.test.ts @@ -93,7 +93,7 @@ describe('create timelines', () => { createTimelinesRoute(server.router, createMockConfig(), securitySetup); const mockRequest = getCreateTimelinesRequest(createTimelineWithoutTimelineId); - await server.inject(mockRequest, context); + await server.inject(mockRequest, requestContextMock.convertContext(context)); }); test('should Create a new timeline savedObject', async () => { @@ -123,7 +123,7 @@ describe('create timelines', () => { test('returns 200 when create timeline successfully', async () => { const response = await server.inject( getCreateTimelinesRequest(createTimelineWithoutTimelineId), - context + requestContextMock.convertContext(context) ); expect(response.status).toEqual(200); }); @@ -157,7 +157,7 @@ describe('create timelines', () => { test('returns error message', async () => { const response = await server.inject( getCreateTimelinesRequest(createTimelineWithTimelineId), - context + requestContextMock.convertContext(context) ); expect(response.body).toEqual({ message: CREATE_TIMELINE_ERROR_MESSAGE, @@ -195,7 +195,7 @@ describe('create timelines', () => { createTimelinesRoute(server.router, createMockConfig(), securitySetup); const mockRequest = getCreateTimelinesRequest(createTemplateTimelineWithoutTimelineId); - await server.inject(mockRequest, context); + await server.inject(mockRequest, requestContextMock.convertContext(context)); }); test('should Create a new timeline template savedObject', async () => { @@ -227,7 +227,7 @@ describe('create timelines', () => { test('returns 200 when create timeline successfully', async () => { const response = await server.inject( getCreateTimelinesRequest(createTimelineWithoutTimelineId), - context + requestContextMock.convertContext(context) ); expect(response.status).toEqual(200); }); @@ -264,7 +264,7 @@ describe('create timelines', () => { test('returns error message', async () => { const response = await server.inject( getCreateTimelinesRequest(updateTemplateTimelineWithTimelineId), - context + requestContextMock.convertContext(context) ); expect(response.body).toEqual({ message: CREATE_TEMPLATE_TIMELINE_ERROR_MESSAGE, diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/export_timelines/index.test.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/export_timelines/index.test.ts index d117d3d0c1b4..cfd61beb4b17 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/export_timelines/index.test.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/export_timelines/index.test.ts @@ -71,14 +71,20 @@ describe('export timelines', () => { describe('status codes', () => { test('returns 200 when finding selected timelines', async () => { - const response = await server.inject(getExportTimelinesRequest(), context); + const response = await server.inject( + getExportTimelinesRequest(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(200); }); test('catch error when status search throws error', async () => { clients.savedObjectsClient.bulkGet.mockReset(); clients.savedObjectsClient.bulkGet.mockRejectedValue(new Error('Test error')); - const response = await server.inject(getExportTimelinesRequest(), context); + const response = await server.inject( + getExportTimelinesRequest(), + requestContextMock.convertContext(context) + ); expect(response.status).toEqual(500); expect(response.body).toEqual({ message: 'Test error', diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/get_timeline/index.test.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/get_timeline/index.test.ts index 609b26f89be5..c5c8ab6bfb7f 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/get_timeline/index.test.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/get_timeline/index.test.ts @@ -49,7 +49,10 @@ describe('get timeline', () => { test('should call getTimelineTemplateOrNull if templateTimelineId is given', async () => { const templateTimelineId = '123'; - await server.inject(getTimelineRequest({ template_timeline_id: templateTimelineId }), context); + await server.inject( + getTimelineRequest({ template_timeline_id: templateTimelineId }), + requestContextMock.convertContext(context) + ); expect((getTimelineTemplateOrNull as jest.Mock).mock.calls[0][1]).toEqual(templateTimelineId); }); @@ -57,13 +60,16 @@ describe('get timeline', () => { test('should call getTimelineOrNull if id is given', async () => { const id = '456'; - await server.inject(getTimelineRequest({ id }), context); + await server.inject(getTimelineRequest({ id }), requestContextMock.convertContext(context)); expect((getTimelineOrNull as jest.Mock).mock.calls[0][1]).toEqual(id); }); test('should throw error message if nither templateTimelineId nor id is given', async () => { - const res = await server.inject(getTimelineRequest(), context); + const res = await server.inject( + getTimelineRequest(), + requestContextMock.convertContext(context) + ); expect(res.body.message).toEqual('please provide id or template_timeline_id'); }); }); diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/get_timelines/index.test.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/get_timelines/index.test.ts index 0298a3f869bd..208b804f222d 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/get_timelines/index.test.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/get_timelines/index.test.ts @@ -46,13 +46,13 @@ describe('get all timelines', () => { }); test('should get the total count', async () => { - await server.inject(getTimelineRequest(), context); + await server.inject(getTimelineRequest(), requestContextMock.convertContext(context)); expect((getAllTimeline as jest.Mock).mock.calls[0][2]).toEqual({ pageSize: 1, pageIndex: 1 }); }); test('should get all timelines with total count', async () => { (getAllTimeline as jest.Mock).mockResolvedValue({ totalCount: 100 }); - await server.inject(getTimelineRequest(), context); + await server.inject(getTimelineRequest(), requestContextMock.convertContext(context)); expect((getAllTimeline as jest.Mock).mock.calls[1][2]).toEqual({ pageSize: 100, pageIndex: 1 }); }); }); diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/import_timelines/index.test.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/import_timelines/index.test.ts index d92d82ea82fc..9148c8f5441d 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/import_timelines/index.test.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/import_timelines/index.test.ts @@ -158,31 +158,31 @@ describe('import timelines', () => { test('should use given timelineId to check if the timeline savedObject already exist', async () => { const mockRequest = await getImportTimelinesRequest(); - await server.inject(mockRequest, context); + await server.inject(mockRequest, requestContextMock.convertContext(context)); expect(mockGetTimeline.mock.calls[0][1]).toEqual(mockUniqueParsedObjects[0].savedObjectId); }); test('should Create a new timeline savedObject', async () => { const mockRequest = await getImportTimelinesRequest(); - await server.inject(mockRequest, context); + await server.inject(mockRequest, requestContextMock.convertContext(context)); expect(mockPersistTimeline).toHaveBeenCalled(); }); test('should Create a new timeline savedObject without timelineId', async () => { const mockRequest = await getImportTimelinesRequest(); - await server.inject(mockRequest, context); + await server.inject(mockRequest, requestContextMock.convertContext(context)); expect(mockPersistTimeline.mock.calls[0][1]).toBeNull(); }); test('should Create a new timeline savedObject without timeline version', async () => { const mockRequest = await getImportTimelinesRequest(); - await server.inject(mockRequest, context); + await server.inject(mockRequest, requestContextMock.convertContext(context)); expect(mockPersistTimeline.mock.calls[0][2]).toBeNull(); }); test('should Create a new timeline savedObject with given timeline', async () => { const mockRequest = await getImportTimelinesRequest(); - await server.inject(mockRequest, context); + await server.inject(mockRequest, requestContextMock.convertContext(context)); expect(mockPersistTimeline.mock.calls[0][3]).toEqual({ ...mockParsedTimelineObject, status: TimelineStatus.active, @@ -202,7 +202,7 @@ describe('import timelines', () => { ], ]); const mockRequest = await getImportTimelinesRequest(); - const response = await server.inject(mockRequest, context); + const response = await server.inject(mockRequest, requestContextMock.convertContext(context)); expect(response.body).toEqual({ success: false, success_count: 0, @@ -222,13 +222,13 @@ describe('import timelines', () => { test('should Create new pinned events', async () => { const mockRequest = await getImportTimelinesRequest(); - await server.inject(mockRequest, context); + await server.inject(mockRequest, requestContextMock.convertContext(context)); expect(mockPersistPinnedEventOnTimeline).toHaveBeenCalled(); }); test('should Create a new pinned event with new timeline id', async () => { const mockRequest = await getImportTimelinesRequest(); - await server.inject(mockRequest, context); + await server.inject(mockRequest, requestContextMock.convertContext(context)); expect(mockPersistPinnedEventOnTimeline.mock.calls[0][1]).toEqual( mockCreatedTimeline.savedObjectId ); @@ -236,7 +236,7 @@ describe('import timelines', () => { test('should Create a new pinned event with pinnedEventId', async () => { const mockRequest = await getImportTimelinesRequest(); - await server.inject(mockRequest, context); + await server.inject(mockRequest, requestContextMock.convertContext(context)); expect(mockPersistPinnedEventOnTimeline.mock.calls[0][2]).toEqual( mockUniqueParsedObjects[0].pinnedEventIds ); @@ -244,7 +244,7 @@ describe('import timelines', () => { test('should Check if note exists', async () => { const mockRequest = await getImportTimelinesRequest(); - await server.inject(mockRequest, context); + await server.inject(mockRequest, requestContextMock.convertContext(context)); expect(mockGetNote.mock.calls[0][1]).toEqual( mockUniqueParsedObjects[0].globalNotes[0].noteId ); @@ -252,19 +252,19 @@ describe('import timelines', () => { test('should Create notes', async () => { const mockRequest = await getImportTimelinesRequest(); - await server.inject(mockRequest, context); + await server.inject(mockRequest, requestContextMock.convertContext(context)); expect(mockPersistNote).toHaveBeenCalled(); }); test('should provide no noteSavedObjectId when Creating notes for a timeline', async () => { const mockRequest = await getImportTimelinesRequest(); - await server.inject(mockRequest, context); + await server.inject(mockRequest, requestContextMock.convertContext(context)); expect(mockPersistNote.mock.calls[0][0].noteId).toBeNull(); }); test('should provide new notes with original author info when Creating notes for a timeline', async () => { const mockRequest = await getImportTimelinesRequest(); - await server.inject(mockRequest, context); + await server.inject(mockRequest, requestContextMock.convertContext(context)); expect(mockPersistNote.mock.calls[0][0].note).toEqual({ eventId: undefined, note: 'original note', @@ -299,7 +299,7 @@ describe('import timelines', () => { mockGetNote.mockRejectedValue(new Error()); const mockRequest = await getImportTimelinesRequest(); - await server.inject(mockRequest, context); + await server.inject(mockRequest, requestContextMock.convertContext(context)); expect(mockPersistNote.mock.calls[0][0].note).toEqual({ created: mockUniqueParsedObjects[0].globalNotes[0].created, createdBy: mockUniqueParsedObjects[0].globalNotes[0].createdBy, @@ -331,7 +331,7 @@ describe('import timelines', () => { test('returns 200 when import timeline successfully', async () => { const mockRequest = await getImportTimelinesRequest(); - const response = await server.inject(mockRequest, context); + const response = await server.inject(mockRequest, requestContextMock.convertContext(context)); expect(response.status).toEqual(200); }); }); @@ -364,7 +364,7 @@ describe('import timelines', () => { test('returns error message', async () => { const mockRequest = await getImportTimelinesRequest(); - const response = await server.inject(mockRequest, context); + const response = await server.inject(mockRequest, requestContextMock.convertContext(context)); expect(response.body).toEqual({ success: false, success_count: 0, @@ -393,7 +393,7 @@ describe('import timelines', () => { ], ]); const mockRequest = await getImportTimelinesRequest(); - const response = await server.inject(mockRequest, context); + const response = await server.inject(mockRequest, requestContextMock.convertContext(context)); expect(response.body).toEqual({ success: false, success_count: 0, @@ -422,7 +422,7 @@ describe('import timelines', () => { ], ]); const mockRequest = await getImportTimelinesRequest(); - const response = await server.inject(mockRequest, context); + const response = await server.inject(mockRequest, requestContextMock.convertContext(context)); expect(response.body).toEqual({ success: false, success_count: 0, @@ -582,7 +582,7 @@ describe('import timeline templates', () => { test('should use given timelineId to check if the timeline savedObject already exist', async () => { const mockRequest = await getImportTimelinesRequest(); - await server.inject(mockRequest, context); + await server.inject(mockRequest, requestContextMock.convertContext(context)); expect(mockGetTimeline.mock.calls[0][1]).toEqual( mockUniqueParsedTemplateTimelineObjects[0].savedObjectId ); @@ -590,7 +590,7 @@ describe('import timeline templates', () => { test('should use given templateTimelineId to check if the timeline savedObject already exist', async () => { const mockRequest = await getImportTimelinesRequest(); - await server.inject(mockRequest, context); + await server.inject(mockRequest, requestContextMock.convertContext(context)); expect(mockGetTemplateTimeline.mock.calls[0][1]).toEqual( mockUniqueParsedTemplateTimelineObjects[0].templateTimelineId ); @@ -598,25 +598,25 @@ describe('import timeline templates', () => { test('should Create a new timeline savedObject', async () => { const mockRequest = await getImportTimelinesRequest(); - await server.inject(mockRequest, context); + await server.inject(mockRequest, requestContextMock.convertContext(context)); expect(mockPersistTimeline).toHaveBeenCalled(); }); test('should Create a new timeline savedObject without timelineId', async () => { const mockRequest = await getImportTimelinesRequest(); - await server.inject(mockRequest, context); + await server.inject(mockRequest, requestContextMock.convertContext(context)); expect(mockPersistTimeline.mock.calls[0][1]).toBeNull(); }); test('should Create a new timeline savedObject without timeline version', async () => { const mockRequest = await getImportTimelinesRequest(); - await server.inject(mockRequest, context); + await server.inject(mockRequest, requestContextMock.convertContext(context)); expect(mockPersistTimeline.mock.calls[0][2]).toBeNull(); }); test('should Create a new timeline savedObject witn given timeline and skip the omitted fields', async () => { const mockRequest = await getImportTimelinesRequest(); - await server.inject(mockRequest, context); + await server.inject(mockRequest, requestContextMock.convertContext(context)); expect(mockPersistTimeline.mock.calls[0][3]).toEqual({ ...mockParsedTemplateTimelineObject, status: TimelineStatus.active, @@ -625,19 +625,19 @@ describe('import timeline templates', () => { test('should NOT Create new pinned events', async () => { const mockRequest = await getImportTimelinesRequest(); - await server.inject(mockRequest, context); + await server.inject(mockRequest, requestContextMock.convertContext(context)); expect(mockPersistPinnedEventOnTimeline).not.toHaveBeenCalled(); }); test('should provide no noteSavedObjectId when Creating notes for a timeline', async () => { const mockRequest = await getImportTimelinesRequest(); - await server.inject(mockRequest, context); + await server.inject(mockRequest, requestContextMock.convertContext(context)); expect(mockPersistNote.mock.calls[0][0].noteId).toBeNull(); }); test('should exclude event notes when creating notes', async () => { const mockRequest = await getImportTimelinesRequest(); - await server.inject(mockRequest, context); + await server.inject(mockRequest, requestContextMock.convertContext(context)); expect(mockPersistNote.mock.calls[0][0].note).toEqual({ eventId: undefined, note: mockUniqueParsedTemplateTimelineObjects[0].globalNotes[0].note, @@ -651,7 +651,7 @@ describe('import timeline templates', () => { test('returns 200 when import timeline successfully', async () => { const mockRequest = await getImportTimelinesRequest(); - const response = await server.inject(mockRequest, context); + const response = await server.inject(mockRequest, requestContextMock.convertContext(context)); expect(response.status).toEqual(200); }); @@ -666,7 +666,7 @@ describe('import timeline templates', () => { ], ]); const mockRequest = await getImportTimelinesRequest(); - await server.inject(mockRequest, context); + await server.inject(mockRequest, requestContextMock.convertContext(context)); expect(mockPersistTimeline.mock.calls[0][3].templateTimelineId).toEqual( mockNewTemplateTimelineId ); @@ -684,7 +684,7 @@ describe('import timeline templates', () => { ], ]); const mockRequest = await getImportTimelinesRequest(); - const result = await server.inject(mockRequest, context); + const result = await server.inject(mockRequest, requestContextMock.convertContext(context)); expect(result.body).toEqual({ errors: [], success: true, @@ -727,7 +727,7 @@ describe('import timeline templates', () => { test('should use given timelineId to check if the timeline savedObject already exist', async () => { const mockRequest = await getImportTimelinesRequest(); - await server.inject(mockRequest, context); + await server.inject(mockRequest, requestContextMock.convertContext(context)); expect(mockGetTimeline.mock.calls[0][1]).toEqual( mockUniqueParsedTemplateTimelineObjects[0].savedObjectId ); @@ -735,7 +735,7 @@ describe('import timeline templates', () => { test('should use given templateTimelineId to check if the timeline savedObject already exist', async () => { const mockRequest = await getImportTimelinesRequest(); - await server.inject(mockRequest, context); + await server.inject(mockRequest, requestContextMock.convertContext(context)); expect(mockGetTemplateTimeline.mock.calls[0][1]).toEqual( mockUniqueParsedTemplateTimelineObjects[0].templateTimelineId ); @@ -743,13 +743,13 @@ describe('import timeline templates', () => { test('should UPDATE timeline savedObject', async () => { const mockRequest = await getImportTimelinesRequest(); - await server.inject(mockRequest, context); + await server.inject(mockRequest, requestContextMock.convertContext(context)); expect(mockPersistTimeline).toHaveBeenCalled(); }); test('should UPDATE timeline savedObject with timelineId', async () => { const mockRequest = await getImportTimelinesRequest(); - await server.inject(mockRequest, context); + await server.inject(mockRequest, requestContextMock.convertContext(context)); expect(mockPersistTimeline.mock.calls[0][1]).toEqual( mockUniqueParsedTemplateTimelineObjects[0].savedObjectId ); @@ -757,7 +757,7 @@ describe('import timeline templates', () => { test('should UPDATE timeline savedObject without timeline version', async () => { const mockRequest = await getImportTimelinesRequest(); - await server.inject(mockRequest, context); + await server.inject(mockRequest, requestContextMock.convertContext(context)); expect(mockPersistTimeline.mock.calls[0][2]).toEqual( mockUniqueParsedTemplateTimelineObjects[0].version ); @@ -765,25 +765,25 @@ describe('import timeline templates', () => { test('should UPDATE a new timeline savedObject witn given timeline and skip the omitted fields', async () => { const mockRequest = await getImportTimelinesRequest(); - await server.inject(mockRequest, context); + await server.inject(mockRequest, requestContextMock.convertContext(context)); expect(mockPersistTimeline.mock.calls[0][3]).toEqual(mockParsedTemplateTimelineObject); }); test('should NOT Create new pinned events', async () => { const mockRequest = await getImportTimelinesRequest(); - await server.inject(mockRequest, context); + await server.inject(mockRequest, requestContextMock.convertContext(context)); expect(mockPersistPinnedEventOnTimeline).not.toHaveBeenCalled(); }); test('should provide noteSavedObjectId when Creating notes for a timeline', async () => { const mockRequest = await getImportTimelinesRequest(); - await server.inject(mockRequest, context); + await server.inject(mockRequest, requestContextMock.convertContext(context)); expect(mockPersistNote.mock.calls[0][0].noteId).toBeNull(); }); test('should exclude event notes when creating notes', async () => { const mockRequest = await getImportTimelinesRequest(); - await server.inject(mockRequest, context); + await server.inject(mockRequest, requestContextMock.convertContext(context)); expect(mockPersistNote.mock.calls[0][0].note).toEqual({ eventId: undefined, note: mockUniqueParsedTemplateTimelineObjects[0].globalNotes[0].note, @@ -797,7 +797,7 @@ describe('import timeline templates', () => { test('returns 200 when import timeline successfully', async () => { const mockRequest = await getImportTimelinesRequest(); - const response = await server.inject(mockRequest, context); + const response = await server.inject(mockRequest, requestContextMock.convertContext(context)); expect(response.status).toEqual(200); }); @@ -812,7 +812,7 @@ describe('import timeline templates', () => { ], ]); const mockRequest = await getImportTimelinesRequest(); - const response = await server.inject(mockRequest, context); + const response = await server.inject(mockRequest, requestContextMock.convertContext(context)); expect(response.body).toEqual({ success: false, success_count: 0, @@ -841,7 +841,7 @@ describe('import timeline templates', () => { ], ]); const mockRequest = await getImportTimelinesRequest(); - const response = await server.inject(mockRequest, context); + const response = await server.inject(mockRequest, requestContextMock.convertContext(context)); expect(response.body).toEqual({ success: false, success_count: 0, diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/import_timelines/index.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/import_timelines/index.ts index c06d99adf04d..4336c9fd9341 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/import_timelines/index.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/import_timelines/index.ts @@ -46,7 +46,7 @@ export const importTimelinesRoute = ( async (context, request, response) => { try { const siemResponse = buildSiemResponse(response); - const savedObjectsClient = context.core.savedObjects.client; + const savedObjectsClient = (await context.core).savedObjects.client; if (!savedObjectsClient) { return siemResponse.error({ statusCode: 404 }); } diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/patch_timelines/index.test.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/patch_timelines/index.test.ts index ca3482591407..1c7ab73cc380 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/patch_timelines/index.test.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/patch_timelines/index.test.ts @@ -90,7 +90,7 @@ describe('update timelines', () => { patchTimelinesRoute(server.router, createMockConfig(), securitySetup); const mockRequest = getUpdateTimelinesRequest(updateTimelineWithTimelineId); - await server.inject(mockRequest, context); + await server.inject(mockRequest, requestContextMock.convertContext(context)); }); test('should Check if given timeline id exist', async () => { @@ -122,7 +122,7 @@ describe('update timelines', () => { test('returns 200 when create timeline successfully', async () => { const response = await server.inject( getUpdateTimelinesRequest(updateTimelineWithTimelineId), - context + requestContextMock.convertContext(context) ); expect(response.status).toEqual(200); }); @@ -157,7 +157,7 @@ describe('update timelines', () => { test('returns error message', async () => { const response = await server.inject( getUpdateTimelinesRequest(updateTimelineWithTimelineId), - context + requestContextMock.convertContext(context) ); expect(response.body).toEqual({ message: UPDATE_TIMELINE_ERROR_MESSAGE, @@ -198,7 +198,7 @@ describe('update timelines', () => { patchTimelinesRoute(server.router, createMockConfig(), securitySetup); const mockRequest = getUpdateTimelinesRequest(updateTemplateTimelineWithTimelineId); - await server.inject(mockRequest, context); + await server.inject(mockRequest, requestContextMock.convertContext(context)); }); test('should Check if given timeline id exist', async () => { @@ -242,7 +242,7 @@ describe('update timelines', () => { test('returns 200 when create timeline template successfully', async () => { const response = await server.inject( getUpdateTimelinesRequest(updateTemplateTimelineWithTimelineId), - context + requestContextMock.convertContext(context) ); expect(response.status).toEqual(200); }); @@ -277,7 +277,7 @@ describe('update timelines', () => { test('returns error message', async () => { const response = await server.inject( getUpdateTimelinesRequest(updateTemplateTimelineWithTimelineId), - context + requestContextMock.convertContext(context) ); expect(response.body).toEqual({ message: UPDATE_TEMPLATE_TIMELINE_ERROR_MESSAGE, diff --git a/x-pack/plugins/security_solution/server/lib/timeline/saved_object/notes/saved_object.ts b/x-pack/plugins/security_solution/server/lib/timeline/saved_object/notes/saved_object.ts index ecf7b04bc1b1..64820711d668 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/saved_object/notes/saved_object.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/saved_object/notes/saved_object.ts @@ -34,7 +34,7 @@ import { timelineSavedObjectType } from '../../saved_object_mappings'; import { noteFieldsMigrator } from './field_migrator'; export const deleteNote = async (request: FrameworkRequest, noteIds: string[]) => { - const savedObjectsClient = request.context.core.savedObjects.client; + const savedObjectsClient = (await request.context.core).savedObjects.client; await Promise.all( noteIds.map((noteId) => savedObjectsClient.delete(noteSavedObjectType, noteId)) @@ -47,7 +47,7 @@ export const deleteNoteByTimelineId = async (request: FrameworkRequest, timeline hasReference: { type: timelineSavedObjectType, id: timelineId }, }; const notesToBeDeleted = await getAllSavedNote(request, options); - const savedObjectsClient = request.context.core.savedObjects.client; + const savedObjectsClient = (await request.context.core).savedObjects.client; await Promise.all( notesToBeDeleted.notes.map((note) => @@ -159,7 +159,7 @@ const createNote = async ({ note: SavedNote; overrideOwner?: boolean; }) => { - const savedObjectsClient = request.context.core.savedObjects.client; + const savedObjectsClient = (await request.context.core).savedObjects.client; const userInfo = request.user; const shallowCopyOfNote = { ...note }; @@ -217,7 +217,7 @@ const updateNote = async ({ note: SavedNote; overrideOwner?: boolean; }) => { - const savedObjectsClient = request.context.core.savedObjects.client; + const savedObjectsClient = (await request.context.core).savedObjects.client; const userInfo = request.user; const existingNote = await savedObjectsClient.get( @@ -258,7 +258,7 @@ const updateNote = async ({ }; const getSavedNote = async (request: FrameworkRequest, NoteId: string) => { - const savedObjectsClient = request.context.core.savedObjects.client; + const savedObjectsClient = (await request.context.core).savedObjects.client; const savedObject = await savedObjectsClient.get( noteSavedObjectType, NoteId @@ -270,7 +270,7 @@ const getSavedNote = async (request: FrameworkRequest, NoteId: string) => { }; const getAllSavedNote = async (request: FrameworkRequest, options: SavedObjectsFindOptions) => { - const savedObjectsClient = request.context.core.savedObjects.client; + const savedObjectsClient = (await request.context.core).savedObjects.client; const savedObjects = await savedObjectsClient.find(options); return { diff --git a/x-pack/plugins/security_solution/server/lib/timeline/saved_object/pinned_events/index.ts b/x-pack/plugins/security_solution/server/lib/timeline/saved_object/pinned_events/index.ts index 3253deb0194c..59d83ee93e5a 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/saved_object/pinned_events/index.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/saved_object/pinned_events/index.ts @@ -70,7 +70,7 @@ export const deletePinnedEventOnTimeline = async ( request: FrameworkRequest, pinnedEventIds: string[] ) => { - const savedObjectsClient = request.context.core.savedObjects.client; + const savedObjectsClient = (await request.context.core).savedObjects.client; await Promise.all( pinnedEventIds.map((pinnedEventId) => @@ -83,7 +83,7 @@ export const deleteAllPinnedEventsOnTimeline = async ( request: FrameworkRequest, timelineId: string ) => { - const savedObjectsClient = request.context.core.savedObjects.client; + const savedObjectsClient = (await request.context.core).savedObjects.client; const options: SavedObjectsFindOptions = { type: pinnedEventSavedObjectType, search: timelineId, @@ -186,7 +186,7 @@ const getValidTimelineIdAndVersion = async ( }; } - const savedObjectsClient = request.context.core.savedObjects.client; + const savedObjectsClient = (await request.context.core).savedObjects.client; // create timeline because it didn't exist const { timeline: timelineResult } = await createTimeline({ @@ -224,7 +224,7 @@ const createPinnedEvent = async ({ timelineId: string; timelineVersion?: string; }) => { - const savedObjectsClient = request.context.core.savedObjects.client; + const savedObjectsClient = (await request.context.core).savedObjects.client; const savedPinnedEvent: SavedPinnedEvent = { eventId, @@ -252,7 +252,7 @@ const createPinnedEvent = async ({ }; const getSavedPinnedEvent = async (request: FrameworkRequest, pinnedEventId: string) => { - const savedObjectsClient = request.context.core.savedObjects.client; + const savedObjectsClient = (await request.context.core).savedObjects.client; const savedObject = await savedObjectsClient.get( pinnedEventSavedObjectType, pinnedEventId @@ -267,7 +267,7 @@ const getAllSavedPinnedEvents = async ( request: FrameworkRequest, options: SavedObjectsFindOptions ) => { - const savedObjectsClient = request.context.core.savedObjects.client; + const savedObjectsClient = (await request.context.core).savedObjects.client; const savedObjects = await savedObjectsClient.find(options); return savedObjects.saved_objects.map((savedObject) => { diff --git a/x-pack/plugins/security_solution/server/lib/timeline/saved_object/timelines/index.ts b/x-pack/plugins/security_solution/server/lib/timeline/saved_object/timelines/index.ts index 2c573d72dd6e..de7c31fb47a9 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/saved_object/timelines/index.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/saved_object/timelines/index.ts @@ -380,7 +380,7 @@ export const persistTimeline = async ( timeline: SavedTimeline, isImmutable?: boolean ): Promise => { - const savedObjectsClient = request.context.core.savedObjects.client; + const savedObjectsClient = (await request.context.core).savedObjects.client; const userInfo = isImmutable ? ({ username: 'Elastic' } as AuthenticatedUser) : request.user; try { if (timelineId == null) { @@ -498,7 +498,7 @@ const updatePartialSavedTimeline = async ( timelineId: string, timeline: SavedTimeline ) => { - const savedObjectsClient = request.context.core.savedObjects.client; + const savedObjectsClient = (await request.context.core).savedObjects.client; const currentSavedTimeline = await savedObjectsClient.get( timelineSavedObjectType, timelineId @@ -563,7 +563,7 @@ export const resetTimeline = async ( }; export const deleteTimeline = async (request: FrameworkRequest, timelineIds: string[]) => { - const savedObjectsClient = request.context.core.savedObjects.client; + const savedObjectsClient = (await request.context.core).savedObjects.client; await Promise.all( timelineIds.map((timelineId) => @@ -577,7 +577,7 @@ export const deleteTimeline = async (request: FrameworkRequest, timelineIds: str }; const resolveBasicSavedTimeline = async (request: FrameworkRequest, timelineId: string) => { - const savedObjectsClient = request.context.core.savedObjects.client; + const savedObjectsClient = (await request.context.core).savedObjects.client; const { saved_object: savedObject, ...resolveAttributes } = await savedObjectsClient.resolve( timelineSavedObjectType, @@ -615,7 +615,7 @@ const resolveSavedTimeline = async (request: FrameworkRequest, timelineId: strin }; const getBasicSavedTimeline = async (request: FrameworkRequest, timelineId: string) => { - const savedObjectsClient = request.context.core.savedObjects.client; + const savedObjectsClient = (await request.context.core).savedObjects.client; const savedObject = await savedObjectsClient.get( timelineSavedObjectType, timelineId @@ -644,7 +644,7 @@ const getSavedTimeline = async (request: FrameworkRequest, timelineId: string) = const getAllSavedTimeline = async (request: FrameworkRequest, options: SavedObjectsFindOptions) => { const userName = request.user?.username ?? UNAUTHENTICATED_USER; - const savedObjectsClient = request.context.core.savedObjects.client; + const savedObjectsClient = (await request.context.core).savedObjects.client; const savedObjects = await savedObjectsClient.find(options); @@ -693,7 +693,7 @@ export const getSelectedTimelines = async ( request: FrameworkRequest, timelineIds?: string[] | null ) => { - const savedObjectsClient = request.context.core.savedObjects.client; + const savedObjectsClient = (await request.context.core).savedObjects.client; let exportedIds = timelineIds; if (timelineIds == null || timelineIds.length === 0) { const { timeline: savedAllTimelines } = await getAllTimeline( diff --git a/x-pack/plugins/security_solution/server/lib/timeline/utils/common.ts b/x-pack/plugins/security_solution/server/lib/timeline/utils/common.ts index 958a5bf06171..f05366264ece 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/utils/common.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/utils/common.ts @@ -23,7 +23,7 @@ export const buildFrameworkRequest = async ( security: StartPlugins['security'] | SetupPlugins['security'] | undefined, request: KibanaRequest ): Promise => { - const savedObjectsClient = context.core.savedObjects.client; + const savedObjectsClient = (await context.core).savedObjects.client; const user = await security?.authc.getCurrentUser(request); return set( diff --git a/x-pack/plugins/security_solution/server/lists_integration/endpoint/validators/blocklist_validator.ts b/x-pack/plugins/security_solution/server/lists_integration/endpoint/validators/blocklist_validator.ts index 45d080ab956e..5510f352a414 100644 --- a/x-pack/plugins/security_solution/server/lists_integration/endpoint/validators/blocklist_validator.ts +++ b/x-pack/plugins/security_solution/server/lists_integration/endpoint/validators/blocklist_validator.ts @@ -16,7 +16,7 @@ import { } from '@kbn/lists-plugin/server'; import { BaseValidator } from './base_validator'; import { ExceptionItemLikeOptions } from '../types'; -import { isValidHash } from '../../../../common/endpoint/service/trusted_apps/validations'; +import { isValidHash } from '../../../../common/endpoint/service/artifacts/validations'; import { EndpointArtifactExceptionValidationError } from './errors'; const allowedHashes: Readonly = ['file.hash.md5', 'file.hash.sha1', 'file.hash.sha256']; diff --git a/x-pack/plugins/security_solution/server/lists_integration/endpoint/validators/trusted_app_validator.ts b/x-pack/plugins/security_solution/server/lists_integration/endpoint/validators/trusted_app_validator.ts index b83e536cdee1..af98ba707653 100644 --- a/x-pack/plugins/security_solution/server/lists_integration/endpoint/validators/trusted_app_validator.ts +++ b/x-pack/plugins/security_solution/server/lists_integration/endpoint/validators/trusted_app_validator.ts @@ -19,7 +19,7 @@ import { TrustedAppConditionEntry as ConditionEntry } from '../../../../common/e import { getDuplicateFields, isValidHash, -} from '../../../../common/endpoint/service/trusted_apps/validations'; +} from '../../../../common/endpoint/service/artifacts/validations'; import { EndpointArtifactExceptionValidationError } from './errors'; const ProcessHashField = schema.oneOf([ diff --git a/x-pack/plugins/security_solution/server/plugin.ts b/x-pack/plugins/security_solution/server/plugin.ts index a313a0b86a24..cbf2a596e962 100644 --- a/x-pack/plugins/security_solution/server/plugin.ts +++ b/x-pack/plugins/security_solution/server/plugin.ts @@ -8,7 +8,6 @@ import { Observable } from 'rxjs'; import LRU from 'lru-cache'; import { - SIGNALS_ID, QUERY_RULE_TYPE_ID, INDICATOR_RULE_TYPE_ID, ML_RULE_TYPE_ID, @@ -177,9 +176,6 @@ export class Plugin implements ISecuritySolutionPlugin { this.telemetryUsageCounter = plugins.usageCollection?.createUsageCounter(APP_ID); - // TODO: Once we are past experimental phase this check can be removed along with legacy registration of rules - const isRuleRegistryEnabled = experimentalFeatures.ruleRegistryEnabled; - const { ruleDataService } = plugins.ruleRegistry; let ruleDataClient: IRuleDataClient | null = null; let previewRuleDataClient: IRuleDataClient | null = null; @@ -271,7 +267,8 @@ export class Plugin implements ISecuritySolutionPlugin { registerPolicyRoutes(router, endpointContext); registerActionRoutes(router, endpointContext); - const racRuleTypes = [ + const ruleTypes = [ + LEGACY_NOTIFICATIONS_ID, EQL_RULE_TYPE_ID, INDICATOR_RULE_TYPE_ID, ML_RULE_TYPE_ID, @@ -279,11 +276,6 @@ export class Plugin implements ISecuritySolutionPlugin { SAVED_QUERY_RULE_TYPE_ID, THRESHOLD_RULE_TYPE_ID, ]; - const ruleTypes = [ - SIGNALS_ID, - LEGACY_NOTIFICATIONS_ID, - ...(isRuleRegistryEnabled ? racRuleTypes : []), - ]; plugins.features.registerKibanaFeature(getKibanaPrivilegesFeaturePrivileges(ruleTypes)); plugins.features.registerKibanaFeature(getCasesKibanaFeature()); diff --git a/x-pack/plugins/security_solution/server/request_context_factory.ts b/x-pack/plugins/security_solution/server/request_context_factory.ts index b0fe00485e2a..9f6b950c4620 100644 --- a/x-pack/plugins/security_solution/server/request_context_factory.ts +++ b/x-pack/plugins/security_solution/server/request_context_factory.ts @@ -72,11 +72,14 @@ export class RequestContextFactory implements IRequestContextFactory { // If Fleet is enabled, then get its Authz if (startPlugins.fleet) { - fleetAuthz = context.fleet?.authz ?? (await startPlugins.fleet?.authz.fromRequest(request)); + fleetAuthz = + (await context.fleet)?.authz ?? (await startPlugins.fleet?.authz.fromRequest(request)); } + const coreContext = await context.core; + return { - core: context.core, + core: coreContext, get endpointAuthz(): Immutable { // Lazy getter of endpoint Authz. No point in defining it if it is never used. @@ -105,7 +108,7 @@ export class RequestContextFactory implements IRequestContextFactory { getRuleExecutionLog: memoize(() => ruleExecutionLogForRoutesFactory( - context.core.savedObjects.client, + coreContext.savedObjects.client, startPlugins.eventLog.getClient(request), logger ) @@ -117,7 +120,7 @@ export class RequestContextFactory implements IRequestContextFactory { } const username = security?.authc.getCurrentUser(request)?.username || 'elastic'; - return lists.getExceptionListClient(context.core.savedObjects.client, username); + return lists.getExceptionListClient(coreContext.savedObjects.client, username); }, }; } diff --git a/x-pack/plugins/security_solution/server/routes/index.ts b/x-pack/plugins/security_solution/server/routes/index.ts index fe093b0b31ee..8763f2d00669 100644 --- a/x-pack/plugins/security_solution/server/routes/index.ts +++ b/x-pack/plugins/security_solution/server/routes/index.ts @@ -86,15 +86,14 @@ export const initRoutes = ( previewRuleDataClient: IRuleDataClient, previewTelemetryReceiver: ITelemetryReceiver ) => { - const isRuleRegistryEnabled = ruleDataClient != null; // Detection Engine Rule routes that have the REST endpoints of /api/detection_engine/rules // All REST rule creation, deletion, updating, etc - createRulesRoute(router, ml, isRuleRegistryEnabled); - readRulesRoute(router, logger, isRuleRegistryEnabled); - updateRulesRoute(router, ml, isRuleRegistryEnabled); - patchRulesRoute(router, ml, isRuleRegistryEnabled); - deleteRulesRoute(router, isRuleRegistryEnabled); - findRulesRoute(router, logger, isRuleRegistryEnabled); + createRulesRoute(router, ml); + readRulesRoute(router, logger); + updateRulesRoute(router, ml); + patchRulesRoute(router, ml); + deleteRulesRoute(router); + findRulesRoute(router, logger); previewRulesRoute( router, config, @@ -110,19 +109,19 @@ export const initRoutes = ( legacyCreateLegacyNotificationRoute(router, logger); addPrepackedRulesRoute(router); - getPrepackagedRulesStatusRoute(router, config, security, isRuleRegistryEnabled); - createRulesBulkRoute(router, ml, isRuleRegistryEnabled, logger); - updateRulesBulkRoute(router, ml, isRuleRegistryEnabled, logger); - patchRulesBulkRoute(router, ml, isRuleRegistryEnabled, logger); - deleteRulesBulkRoute(router, isRuleRegistryEnabled, logger); - performBulkActionRoute(router, ml, logger, isRuleRegistryEnabled); + getPrepackagedRulesStatusRoute(router, config, security); + createRulesBulkRoute(router, ml, logger); + updateRulesBulkRoute(router, ml, logger); + patchRulesBulkRoute(router, ml, logger); + deleteRulesBulkRoute(router, logger); + performBulkActionRoute(router, ml, logger); getRuleExecutionEventsRoute(router); createTimelinesRoute(router, config, security); patchTimelinesRoute(router, config, security); - importRulesRoute(router, config, ml, isRuleRegistryEnabled); - exportRulesRoute(router, config, logger, isRuleRegistryEnabled); + importRulesRoute(router, config, ml); + exportRulesRoute(router, config, logger); importTimelinesRoute(router, config, security); exportTimelinesRoute(router, config, security); @@ -156,7 +155,7 @@ export const initRoutes = ( deleteIndexRoute(router); // Detection Engine tags routes that have the REST endpoints of /api/detection_engine/tags - readTagsRoute(router, isRuleRegistryEnabled); + readTagsRoute(router); // Privileges API to get the generic user privileges readPrivilegesRoute(router, hasEncryptionKey); diff --git a/x-pack/plugins/security_solution/server/types.ts b/x-pack/plugins/security_solution/server/types.ts index c0e496ba4e64..b1bf51439a3f 100644 --- a/x-pack/plugins/security_solution/server/types.ts +++ b/x-pack/plugins/security_solution/server/types.ts @@ -5,7 +5,11 @@ * 2.0. */ -import type { IRouter, RequestHandlerContext } from '@kbn/core/server'; +import type { + IRouter, + CustomRequestHandlerContext, + CoreRequestHandlerContext, +} from '@kbn/core/server'; import type { ActionsApiRequestHandlerContext } from '@kbn/actions-plugin/server'; import type { AlertingApiRequestHandlerContext } from '@kbn/alerting-plugin/server'; import type { FleetRequestHandlerContext } from '@kbn/fleet-plugin/server'; @@ -21,7 +25,8 @@ import { EndpointAuthz } from '../common/endpoint/types/authz'; export { AppClient }; -export interface SecuritySolutionApiRequestHandlerContext extends RequestHandlerContext { +export interface SecuritySolutionApiRequestHandlerContext { + core: CoreRequestHandlerContext; endpointAuthz: EndpointAuthz; getConfig: () => ConfigType; getFrameworkRequest: () => FrameworkRequest; @@ -32,13 +37,13 @@ export interface SecuritySolutionApiRequestHandlerContext extends RequestHandler getExceptionListClient: () => ExceptionListClient | null; } -export interface SecuritySolutionRequestHandlerContext extends RequestHandlerContext { +export type SecuritySolutionRequestHandlerContext = CustomRequestHandlerContext<{ securitySolution: SecuritySolutionApiRequestHandlerContext; actions: ActionsApiRequestHandlerContext; alerting: AlertingApiRequestHandlerContext; licensing: LicensingApiRequestHandlerContext; lists?: ListsApiRequestHandlerContext; fleet?: FleetRequestHandlerContext['fleet']; -} +}>; export type SecuritySolutionPluginRouter = IRouter; diff --git a/x-pack/plugins/session_view/common/constants.ts b/x-pack/plugins/session_view/common/constants.ts index 0e1a7c16172f..98c27bd4f86e 100644 --- a/x-pack/plugins/session_view/common/constants.ts +++ b/x-pack/plugins/session_view/common/constants.ts @@ -13,7 +13,7 @@ export const PROCESS_EVENTS_INDEX = 'logs-endpoint.events.process-*'; export const PREVIEW_ALERTS_INDEX = '.preview.alerts-security.alerts-default'; export const ENTRY_SESSION_ENTITY_ID_PROPERTY = 'process.entry_leader.entity_id'; export const ALERT_UUID_PROPERTY = 'kibana.alert.uuid'; -export const KIBANA_DATE_FORMAT = 'MMM DD, YYYY @ hh:mm:ss.SSS'; +export const KIBANA_DATE_FORMAT = 'MMM DD, YYYY @ HH:mm:ss.SSS'; export const ALERT_STATUS = { OPEN: 'open', ACKNOWLEDGED: 'acknowledged', diff --git a/x-pack/plugins/session_view/public/components/detail_panel_copy/index.tsx b/x-pack/plugins/session_view/public/components/detail_panel_copy/index.tsx index 0716ded33a7d..fb25e85e19ca 100644 --- a/x-pack/plugins/session_view/public/components/detail_panel_copy/index.tsx +++ b/x-pack/plugins/session_view/public/components/detail_panel_copy/index.tsx @@ -5,7 +5,7 @@ * 2.0. */ import React, { ReactNode } from 'react'; -import { EuiButtonIcon, EuiCopy } from '@elastic/eui'; +import { EuiButtonIcon, EuiCopy, EuiToolTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { DetailPanelListItem } from '../detail_panel_list_item'; import { dataOrDash } from '../../utils/data_or_dash'; @@ -55,5 +55,11 @@ export const DetailPanelCopy = ({ props.display = display; } - return {children}; + return ( + + + <>{children} + + + ); }; diff --git a/x-pack/plugins/session_view/public/components/detail_panel_list_item/index.tsx b/x-pack/plugins/session_view/public/components/detail_panel_list_item/index.tsx index 93a6554bbe54..3ba8dee8965f 100644 --- a/x-pack/plugins/session_view/public/components/detail_panel_list_item/index.tsx +++ b/x-pack/plugins/session_view/public/components/detail_panel_list_item/index.tsx @@ -27,7 +27,7 @@ interface EuiTextPropsCss extends EuiTextProps { export const DetailPanelListItem = ({ children, copy, - display = 'flex', + display = 'grid', }: DetailPanelListItemDeps) => { const [isHovered, setIsHovered] = useState(false); const styles = useStyles({ display }); diff --git a/x-pack/plugins/session_view/public/components/detail_panel_list_item/styles.ts b/x-pack/plugins/session_view/public/components/detail_panel_list_item/styles.ts index 22f5e6782288..4e15221638f9 100644 --- a/x-pack/plugins/session_view/public/components/detail_panel_list_item/styles.ts +++ b/x-pack/plugins/session_view/public/components/detail_panel_list_item/styles.ts @@ -19,7 +19,7 @@ export const useStyles = ({ display }: StylesDeps) => { const cached = useMemo(() => { const item: CSSObject = { display, - alignItems: 'center', + alignContent: 'center', padding: `0px ${euiTheme.size.s} `, width: '100%', fontWeight: 'inherit', @@ -27,6 +27,13 @@ export const useStyles = ({ display }: StylesDeps) => { lineHeight: euiTheme.size.l, letterSpacing: '0px', textAlign: 'left', + + '& .euiToolTipAnchor': { + width: `calc(100% - ${euiTheme.size.xl})`, + whiteSpace: 'nowrap', + overflow: 'hidden', + textOverflow: 'ellipsis', + }, }; const copiableItem: CSSObject = { diff --git a/x-pack/plugins/session_view/public/components/detail_panel_process_tab/index.tsx b/x-pack/plugins/session_view/public/components/detail_panel_process_tab/index.tsx index b148fc99ef7a..a310784d7473 100644 --- a/x-pack/plugins/session_view/public/components/detail_panel_process_tab/index.tsx +++ b/x-pack/plugins/session_view/public/components/detail_panel_process_tab/index.tsx @@ -99,11 +99,7 @@ export const DetailPanelProcessTab = ({ processDetail }: DetailPanelProcessTabDe }, { title: args, - description: ( - - {leaderArgs} - - ), + description: {leaderArgs}, }, { title: interactive, @@ -137,19 +133,11 @@ export const DetailPanelProcessTab = ({ processDetail }: DetailPanelProcessTabDe }, { title: start, - description: ( - - {dataOrDash(start)} - - ), + description: {dataOrDash(start)}, }, { title: end, - description: ( - - {dataOrDash(end)} - - ), + description: {dataOrDash(end)}, }, { title: exit_code, @@ -164,17 +152,13 @@ export const DetailPanelProcessTab = ({ processDetail }: DetailPanelProcessTabDe { title: user.name, description: ( - - {dataOrDash(userName)} - + {dataOrDash(userName)} ), }, { title: group.name, description: ( - - {dataOrDash(groupName)} - + {dataOrDash(groupName)} ), }, ]; @@ -195,7 +179,7 @@ export const DetailPanelProcessTab = ({ processDetail }: DetailPanelProcessTabDe title: entry_meta.source.ip, description: ( - {dataOrDash(entryMetaSourceIp)} + {dataOrDash(entryMetaSourceIp)} ), } @@ -242,11 +226,7 @@ export const DetailPanelProcessTab = ({ processDetail }: DetailPanelProcessTabDe }, { title: args, - description: ( - - {processArgs} - - ), + description: {processArgs}, }, { title: executable, @@ -258,7 +238,7 @@ export const DetailPanelProcessTab = ({ processDetail }: DetailPanelProcessTabDe {executable.map((execTuple, idx) => { const [exec, eventAction] = execTuple; return ( -
+
{dataOrDash(exec)} @@ -303,19 +283,11 @@ export const DetailPanelProcessTab = ({ processDetail }: DetailPanelProcessTabDe }, { title: start, - description: ( - - {dataOrDash(start)} - - ), + description: {dataOrDash(start)}, }, { title: end, - description: ( - - {dataOrDash(end)} - - ), + description: {dataOrDash(end)}, }, { title: exit_code, @@ -330,17 +302,13 @@ export const DetailPanelProcessTab = ({ processDetail }: DetailPanelProcessTabDe { title: user.name, description: ( - - {dataOrDash(userName)} - + {dataOrDash(userName)} ), }, { title: group.name, description: ( - - {dataOrDash(groupName)} - + {dataOrDash(groupName)} ), }, ]} diff --git a/x-pack/plugins/session_view/public/components/detail_panel_process_tab/styles.ts b/x-pack/plugins/session_view/public/components/detail_panel_process_tab/styles.ts index 8c1154f0c007..e1bc139ba5d8 100644 --- a/x-pack/plugins/session_view/public/components/detail_panel_process_tab/styles.ts +++ b/x-pack/plugins/session_view/public/components/detail_panel_process_tab/styles.ts @@ -13,15 +13,7 @@ export const useStyles = () => { const { euiTheme } = useEuiTheme(); const cached = useMemo(() => { - const description: CSSObject = { - width: `calc(100% - ${euiTheme.size.xl})`, - whiteSpace: 'nowrap', - overflow: 'hidden', - textOverflow: 'ellipsis', - }; - const descriptionSemibold: CSSObject = { - ...description, fontWeight: euiTheme.font.weight.medium, }; @@ -30,10 +22,15 @@ export const useStyles = () => { paddingLeft: euiTheme.size.xs, }; + const ellipsis: CSSObject = { + overflow: 'hidden', + textOverflow: 'ellipsis', + }; + return { - description, descriptionSemibold, executableAction, + ellipsis, }; }, [euiTheme]); diff --git a/x-pack/plugins/session_view/server/routes/process_events_route.ts b/x-pack/plugins/session_view/server/routes/process_events_route.ts index c75bdc3fb6ed..7341f8238f9a 100644 --- a/x-pack/plugins/session_view/server/routes/process_events_route.ts +++ b/x-pack/plugins/session_view/server/routes/process_events_route.ts @@ -27,7 +27,7 @@ export const registerProcessEventsRoute = (router: IRouter) => { }, }, async (context, request, response) => { - const client = context.core.elasticsearch.client.asCurrentUser; + const client = (await context.core).elasticsearch.client.asCurrentUser; const { sessionEntityId, cursor, forward = true } = request.query; const body = await doSearch(client, sessionEntityId, cursor, forward); diff --git a/x-pack/plugins/session_view/server/routes/session_entry_leaders_route.ts b/x-pack/plugins/session_view/server/routes/session_entry_leaders_route.ts index 186195c342a4..c8fd28334f18 100644 --- a/x-pack/plugins/session_view/server/routes/session_entry_leaders_route.ts +++ b/x-pack/plugins/session_view/server/routes/session_entry_leaders_route.ts @@ -19,7 +19,7 @@ export const sessionEntryLeadersRoute = (router: IRouter) => { }, }, async (context, request, response) => { - const client = context.core.elasticsearch.client.asCurrentUser; + const client = (await context.core).elasticsearch.client.asCurrentUser; const { id } = request.query; const result = await client.get({ diff --git a/x-pack/plugins/snapshot_restore/server/routes/api/app.ts b/x-pack/plugins/snapshot_restore/server/routes/api/app.ts index 21938eaecf60..702a7633f7cc 100644 --- a/x-pack/plugins/snapshot_restore/server/routes/api/app.ts +++ b/x-pack/plugins/snapshot_restore/server/routes/api/app.ts @@ -32,7 +32,7 @@ export function registerAppRoutes({ router.get( { path: addBasePath('privileges'), validate: false }, license.guardApiRoute(async (ctx, req, res) => { - const { client: clusterClient } = ctx.core.elasticsearch; + const { client: clusterClient } = (await ctx.core).elasticsearch; const privilegesResult: Privileges = { hasAllPrivileges: true, diff --git a/x-pack/plugins/snapshot_restore/server/routes/api/policy.ts b/x-pack/plugins/snapshot_restore/server/routes/api/policy.ts index 57cb81ca13b9..8068c64ec77b 100644 --- a/x-pack/plugins/snapshot_restore/server/routes/api/policy.ts +++ b/x-pack/plugins/snapshot_restore/server/routes/api/policy.ts @@ -23,7 +23,7 @@ export function registerPolicyRoutes({ router.get( { path: addBasePath('policies'), validate: false }, license.guardApiRoute(async (ctx, req, res) => { - const { client: clusterClient } = ctx.core.elasticsearch; + const { client: clusterClient } = (await ctx.core).elasticsearch; const managedPolicies = await getManagedPolicyNames(clusterClient.asCurrentUser); @@ -53,7 +53,7 @@ export function registerPolicyRoutes({ router.get( { path: addBasePath('policy/{name}'), validate: { params: nameParameterSchema } }, license.guardApiRoute(async (ctx, req, res) => { - const { client: clusterClient } = ctx.core.elasticsearch; + const { client: clusterClient } = (await ctx.core).elasticsearch; const { name } = req.params as TypeOf; try { @@ -80,7 +80,7 @@ export function registerPolicyRoutes({ router.post( { path: addBasePath('policies'), validate: { body: policySchema } }, license.guardApiRoute(async (ctx, req, res) => { - const { client: clusterClient } = ctx.core.elasticsearch; + const { client: clusterClient } = (await ctx.core).elasticsearch; const policy = req.body as TypeOf; const { name } = policy; @@ -120,7 +120,7 @@ export function registerPolicyRoutes({ validate: { params: nameParameterSchema, body: policySchema }, }, license.guardApiRoute(async (ctx, req, res) => { - const { client: clusterClient } = ctx.core.elasticsearch; + const { client: clusterClient } = (await ctx.core).elasticsearch; const { name } = req.params as TypeOf; const policy = req.body as TypeOf; @@ -147,7 +147,7 @@ export function registerPolicyRoutes({ router.delete( { path: addBasePath('policies/{name}'), validate: { params: nameParameterSchema } }, license.guardApiRoute(async (ctx, req, res) => { - const { client: clusterClient } = ctx.core.elasticsearch; + const { client: clusterClient } = (await ctx.core).elasticsearch; const { name } = req.params as TypeOf; const policyNames = name.split(','); @@ -178,7 +178,7 @@ export function registerPolicyRoutes({ router.post( { path: addBasePath('policy/{name}/run'), validate: { params: nameParameterSchema } }, license.guardApiRoute(async (ctx, req, res) => { - const { client: clusterClient } = ctx.core.elasticsearch; + const { client: clusterClient } = (await ctx.core).elasticsearch; const { name } = req.params as TypeOf; try { @@ -197,7 +197,7 @@ export function registerPolicyRoutes({ router.get( { path: addBasePath('policies/indices'), validate: false }, license.guardApiRoute(async (ctx, req, res) => { - const { client: clusterClient } = ctx.core.elasticsearch; + const { client: clusterClient } = (await ctx.core).elasticsearch; try { const response = await clusterClient.asCurrentUser.indices.resolveIndex({ @@ -227,7 +227,7 @@ export function registerPolicyRoutes({ router.get( { path: addBasePath('policies/retention_settings'), validate: false }, license.guardApiRoute(async (ctx, req, res) => { - const { client: clusterClient } = ctx.core.elasticsearch; + const { client: clusterClient } = (await ctx.core).elasticsearch; const { persistent, transient, defaults } = await clusterClient.asCurrentUser.cluster.getSettings({ filter_path: '**.slm.retention*', @@ -257,7 +257,7 @@ export function registerPolicyRoutes({ validate: { body: retentionSettingsSchema }, }, license.guardApiRoute(async (ctx, req, res) => { - const { client: clusterClient } = ctx.core.elasticsearch; + const { client: clusterClient } = (await ctx.core).elasticsearch; const { retentionSchedule } = req.body as TypeOf; try { @@ -282,7 +282,7 @@ export function registerPolicyRoutes({ router.post( { path: addBasePath('policies/retention'), validate: false }, license.guardApiRoute(async (ctx, req, res) => { - const { client: clusterClient } = ctx.core.elasticsearch; + const { client: clusterClient } = (await ctx.core).elasticsearch; const response = await clusterClient.asCurrentUser.slm.executeRetention(); return res.ok({ body: response }); }) diff --git a/x-pack/plugins/snapshot_restore/server/routes/api/repositories.ts b/x-pack/plugins/snapshot_restore/server/routes/api/repositories.ts index 4666870133f1..4e86de4036d5 100644 --- a/x-pack/plugins/snapshot_restore/server/routes/api/repositories.ts +++ b/x-pack/plugins/snapshot_restore/server/routes/api/repositories.ts @@ -43,7 +43,7 @@ export function registerRepositoriesRoutes({ router.get( { path: addBasePath('repositories'), validate: false }, license.guardApiRoute(async (ctx, req, res) => { - const { client: clusterClient } = ctx.core.elasticsearch; + const { client: clusterClient } = (await ctx.core).elasticsearch; const managedRepositoryName = await getManagedRepositoryName(clusterClient.asCurrentUser); let repositoryNames: string[] | undefined; @@ -102,7 +102,7 @@ export function registerRepositoriesRoutes({ router.get( { path: addBasePath('repositories/{name}'), validate: { params: nameParameterSchema } }, license.guardApiRoute(async (ctx, req, res) => { - const { client: clusterClient } = ctx.core.elasticsearch; + const { client: clusterClient } = (await ctx.core).elasticsearch; const { name } = req.params as TypeOf; const managedRepository = await getManagedRepositoryName(clusterClient.asCurrentUser); @@ -157,7 +157,7 @@ export function registerRepositoriesRoutes({ router.get( { path: addBasePath('repository_types'), validate: false }, license.guardApiRoute(async (ctx, req, res) => { - const { client: clusterClient } = ctx.core.elasticsearch; + const { client: clusterClient } = (await ctx.core).elasticsearch; // module repo types are available everywhere out of the box // on-prem repo types are not available on Cloud const types: RepositoryType[] = isCloudEnabled @@ -198,7 +198,7 @@ export function registerRepositoriesRoutes({ validate: { params: nameParameterSchema }, }, license.guardApiRoute(async (ctx, req, res) => { - const { client: clusterClient } = ctx.core.elasticsearch; + const { client: clusterClient } = (await ctx.core).elasticsearch; const { name } = req.params as TypeOf; try { @@ -232,7 +232,7 @@ export function registerRepositoriesRoutes({ validate: { params: nameParameterSchema }, }, license.guardApiRoute(async (ctx, req, res) => { - const { client: clusterClient } = ctx.core.elasticsearch; + const { client: clusterClient } = (await ctx.core).elasticsearch; const { name } = req.params as TypeOf; try { @@ -274,7 +274,7 @@ export function registerRepositoriesRoutes({ router.put( { path: addBasePath('repositories'), validate: { body: repositorySchema } }, license.guardApiRoute(async (ctx, req, res) => { - const { client: clusterClient } = ctx.core.elasticsearch; + const { client: clusterClient } = (await ctx.core).elasticsearch; const { name = '', type = '', settings = {} } = req.body as TypeOf; // Check that repository with the same name doesn't already exist @@ -313,7 +313,7 @@ export function registerRepositoriesRoutes({ validate: { body: repositorySchema, params: nameParameterSchema }, }, license.guardApiRoute(async (ctx, req, res) => { - const { client: clusterClient } = ctx.core.elasticsearch; + const { client: clusterClient } = (await ctx.core).elasticsearch; const { name } = req.params as TypeOf; const { type = '', settings = {} } = req.body as TypeOf; @@ -345,7 +345,7 @@ export function registerRepositoriesRoutes({ router.delete( { path: addBasePath('repositories/{name}'), validate: { params: nameParameterSchema } }, license.guardApiRoute(async (ctx, req, res) => { - const { client: clusterClient } = ctx.core.elasticsearch; + const { client: clusterClient } = (await ctx.core).elasticsearch; const { name } = req.params as TypeOf; const repositoryNames = name.split(','); diff --git a/x-pack/plugins/snapshot_restore/server/routes/api/restore.ts b/x-pack/plugins/snapshot_restore/server/routes/api/restore.ts index ea688273781e..e97a64f26a12 100644 --- a/x-pack/plugins/snapshot_restore/server/routes/api/restore.ts +++ b/x-pack/plugins/snapshot_restore/server/routes/api/restore.ts @@ -24,7 +24,7 @@ export function registerRestoreRoutes({ router.get( { path: addBasePath('restores'), validate: false }, license.guardApiRoute(async (ctx, req, res) => { - const { client: clusterClient } = ctx.core.elasticsearch; + const { client: clusterClient } = (await ctx.core).elasticsearch; try { const snapshotRestores: SnapshotRestore[] = []; @@ -99,7 +99,7 @@ export function registerRestoreRoutes({ validate: { body: restoreSettingsSchema, params: restoreParamsSchema }, }, license.guardApiRoute(async (ctx, req, res) => { - const { client: clusterClient } = ctx.core.elasticsearch; + const { client: clusterClient } = (await ctx.core).elasticsearch; const { repository, snapshot } = req.params as TypeOf; const restoreSettings = req.body as TypeOf; diff --git a/x-pack/plugins/snapshot_restore/server/routes/api/snapshots.ts b/x-pack/plugins/snapshot_restore/server/routes/api/snapshots.ts index 7425ad0c272c..c486180424da 100644 --- a/x-pack/plugins/snapshot_restore/server/routes/api/snapshots.ts +++ b/x-pack/plugins/snapshot_restore/server/routes/api/snapshots.ts @@ -47,7 +47,7 @@ export function registerSnapshotsRoutes({ router.get( { path: addBasePath('snapshots'), validate: { query: snapshotListSchema } }, license.guardApiRoute(async (ctx, req, res) => { - const { client: clusterClient } = ctx.core.elasticsearch; + const { client: clusterClient } = (await ctx.core).elasticsearch; const sortField = sortFieldToESParams[(req.query as TypeOf).sortField]; const sortDirection = (req.query as TypeOf).sortDirection; @@ -176,7 +176,7 @@ export function registerSnapshotsRoutes({ validate: { params: getOneParamsSchema }, }, license.guardApiRoute(async (ctx, req, res) => { - const { client: clusterClient } = ctx.core.elasticsearch; + const { client: clusterClient } = (await ctx.core).elasticsearch; const { repository, snapshot } = req.params as TypeOf; const managedRepository = await getManagedRepositoryName(clusterClient.asCurrentUser); @@ -232,7 +232,7 @@ export function registerSnapshotsRoutes({ router.post( { path: addBasePath('snapshots/bulk_delete'), validate: { body: deleteSchema } }, license.guardApiRoute(async (ctx, req, res) => { - const { client: clusterClient } = ctx.core.elasticsearch; + const { client: clusterClient } = (await ctx.core).elasticsearch; const response: { itemsDeleted: Array<{ snapshot: string; repository: string }>; diff --git a/x-pack/plugins/snapshot_restore/server/types.ts b/x-pack/plugins/snapshot_restore/server/types.ts index 9e7d97aed39e..d5710bb39d4c 100644 --- a/x-pack/plugins/snapshot_restore/server/types.ts +++ b/x-pack/plugins/snapshot_restore/server/types.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { IRouter, RequestHandlerContext, IScopedClusterClient } from '@kbn/core/server'; +import type { IRouter, CustomRequestHandlerContext, IScopedClusterClient } from '@kbn/core/server'; import { LicensingPluginSetup } from '@kbn/licensing-plugin/server'; import { SecurityPluginSetup } from '@kbn/security-plugin/server'; import { CloudSetup } from '@kbn/cloud-plugin/server'; @@ -63,6 +63,6 @@ export interface SnapshotRestoreContext { /** * @internal */ -export interface SnapshotRestoreRequestHandlerContext extends RequestHandlerContext { +export type SnapshotRestoreRequestHandlerContext = CustomRequestHandlerContext<{ snapshotRestore: SnapshotRestoreContext; -} +}>; diff --git a/x-pack/plugins/spaces/server/routes/lib/licensed_route_handler.ts b/x-pack/plugins/spaces/server/routes/lib/licensed_route_handler.ts index 19b163e31e31..254130cc1c51 100644 --- a/x-pack/plugins/spaces/server/routes/lib/licensed_route_handler.ts +++ b/x-pack/plugins/spaces/server/routes/lib/licensed_route_handler.ts @@ -5,23 +5,23 @@ * 2.0. */ -import type { RequestHandler, RequestHandlerContext } from '@kbn/core/server'; +import type { CustomRequestHandlerContext, RequestHandler } from '@kbn/core/server'; import type { LicensingApiRequestHandlerContext } from '@kbn/licensing-plugin/server'; export const createLicensedRouteHandler = < P, Q, B, - Context extends RequestHandlerContext & { licensing: LicensingApiRequestHandlerContext } + Context extends CustomRequestHandlerContext<{ licensing: LicensingApiRequestHandlerContext }> >( handler: RequestHandler ) => { - const licensedRouteHandler: RequestHandler = ( + const licensedRouteHandler: RequestHandler = async ( context, request, responseToolkit ) => { - const { license } = context.licensing; + const { license } = await context.licensing; const licenseCheck = license.check('spaces', 'basic'); if (licenseCheck.state === 'unavailable' || licenseCheck.state === 'invalid') { return responseToolkit.forbidden({ body: { message: licenseCheck.message! } }); diff --git a/x-pack/plugins/spaces/server/routes/views/index.ts b/x-pack/plugins/spaces/server/routes/views/index.ts index de33027f5be4..d0cff27e8543 100644 --- a/x-pack/plugins/spaces/server/routes/views/index.ts +++ b/x-pack/plugins/spaces/server/routes/views/index.ts @@ -26,7 +26,8 @@ export function initSpacesViewsRoutes(deps: ViewRouteDeps) { { path: ENTER_SPACE_PATH, validate: false }, async (context, request, response) => { try { - const defaultRoute = await context.core.uiSettings.client.get('defaultRoute'); + const { uiSettings } = await context.core; + const defaultRoute = await uiSettings.client.get('defaultRoute'); const basePath = deps.basePath.get(request); const url = `${basePath}${defaultRoute}`; diff --git a/x-pack/plugins/spaces/server/types.ts b/x-pack/plugins/spaces/server/types.ts index 789f66fe2cbd..267e7b51377a 100644 --- a/x-pack/plugins/spaces/server/types.ts +++ b/x-pack/plugins/spaces/server/types.ts @@ -5,15 +5,15 @@ * 2.0. */ -import type { IRouter, RequestHandlerContext } from '@kbn/core/server'; +import type { CustomRequestHandlerContext, IRouter } from '@kbn/core/server'; import type { LicensingApiRequestHandlerContext } from '@kbn/licensing-plugin/server'; /** * @internal */ -export interface SpacesRequestHandlerContext extends RequestHandlerContext { +export type SpacesRequestHandlerContext = CustomRequestHandlerContext<{ licensing: LicensingApiRequestHandlerContext; -} +}>; /** * @internal diff --git a/x-pack/plugins/synthetics/common/constants/alerts.ts b/x-pack/plugins/synthetics/common/constants/alerts.ts index ff3cda7887ef..a265d13fb71d 100644 --- a/x-pack/plugins/synthetics/common/constants/alerts.ts +++ b/x-pack/plugins/synthetics/common/constants/alerts.ts @@ -52,3 +52,10 @@ export const CLIENT_ALERT_TYPES = { TLS: 'xpack.uptime.alerts.tlsCertificate', DURATION_ANOMALY: 'xpack.uptime.alerts.durationAnomaly', }; + +export const UPTIME_RULE_TYPES = [ + 'xpack.uptime.alerts.tls', + 'xpack.uptime.alerts.tlsCertificate', + 'xpack.uptime.alerts.monitorStatus', + 'xpack.uptime.alerts.durationAnomaly', +]; diff --git a/x-pack/plugins/synthetics/common/constants/plugin.ts b/x-pack/plugins/synthetics/common/constants/plugin.ts index 8c1506621ba8..619e68b93109 100644 --- a/x-pack/plugins/synthetics/common/constants/plugin.ts +++ b/x-pack/plugins/synthetics/common/constants/plugin.ts @@ -9,16 +9,16 @@ import { i18n } from '@kbn/i18n'; export const PLUGIN = { APP_ROOT_ID: 'react-uptime-root', - DESCRIPTION: i18n.translate('xpack.uptime.pluginDescription', { + DESCRIPTION: i18n.translate('xpack.synthetics.pluginDescription', { defaultMessage: 'Synthetics monitoring', description: 'The description text that will appear in the feature catalogue.', }), ID: 'uptime', - LOCAL_STORAGE_KEY: 'xpack.uptime', - NAME: i18n.translate('xpack.uptime.featureRegistry.syntheticsFeatureName', { + LOCAL_STORAGE_KEY: 'xpack.synthetics.', + NAME: i18n.translate('xpack.synthetics.featureRegistry.syntheticsFeatureName', { defaultMessage: 'Synthetics and Uptime', }), - TITLE: i18n.translate('xpack.uptime.uptimeFeatureCatalogueTitle', { + TITLE: i18n.translate('xpack.synthetics.uptimeFeatureCatalogueTitle', { defaultMessage: 'Uptime', }), }; diff --git a/x-pack/plugins/synthetics/common/translations.ts b/x-pack/plugins/synthetics/common/translations.ts index 2b95732badaf..9bef65bd9dad 100644 --- a/x-pack/plugins/synthetics/common/translations.ts +++ b/x-pack/plugins/synthetics/common/translations.ts @@ -8,38 +8,44 @@ import { i18n } from '@kbn/i18n'; export const VALUE_MUST_BE_GREATER_THAN_ZERO = i18n.translate( - 'xpack.uptime.settings.invalid.error', + 'xpack.synthetics.settings.invalid.error', { defaultMessage: 'Value must be greater than 0.', } ); -export const VALUE_MUST_BE_AN_INTEGER = i18n.translate('xpack.uptime.settings.invalid.nanError', { - defaultMessage: 'Value must be an integer.', -}); +export const VALUE_MUST_BE_AN_INTEGER = i18n.translate( + 'xpack.synthetics.settings.invalid.nanError', + { + defaultMessage: 'Value must be an integer.', + } +); export const MonitorStatusTranslations = { - defaultActionMessage: i18n.translate('xpack.uptime.alerts.monitorStatus.defaultActionMessage', { - defaultMessage: - 'Monitor {monitorName} with url {monitorUrl} from {observerLocation} {statusMessage} The latest error message is {latestErrorMessage}', - values: { - monitorName: '{{state.monitorName}}', - monitorUrl: '{{{state.monitorUrl}}}', - statusMessage: '{{{state.statusMessage}}}', - latestErrorMessage: '{{{state.latestErrorMessage}}}', - observerLocation: '{{state.observerLocation}}', - }, - }), - name: i18n.translate('xpack.uptime.alerts.monitorStatus.clientName', { + defaultActionMessage: i18n.translate( + 'xpack.synthetics.alerts.monitorStatus.defaultActionMessage', + { + defaultMessage: + 'Monitor {monitorName} with url {monitorUrl} from {observerLocation} {statusMessage} The latest error message is {latestErrorMessage}', + values: { + monitorName: '{{state.monitorName}}', + monitorUrl: '{{{state.monitorUrl}}}', + statusMessage: '{{{state.statusMessage}}}', + latestErrorMessage: '{{{state.latestErrorMessage}}}', + observerLocation: '{{state.observerLocation}}', + }, + } + ), + name: i18n.translate('xpack.synthetics.alerts.monitorStatus.clientName', { defaultMessage: 'Uptime monitor status', }), - description: i18n.translate('xpack.uptime.alerts.monitorStatus.description', { + description: i18n.translate('xpack.synthetics.alerts.monitorStatus.description', { defaultMessage: 'Alert when a monitor is down or an availability threshold is breached.', }), }; export const TlsTranslations = { - defaultActionMessage: i18n.translate('xpack.uptime.alerts.tls.defaultActionMessage', { + defaultActionMessage: i18n.translate('xpack.synthetics.alerts.tls.defaultActionMessage', { defaultMessage: `Detected TLS certificate {commonName} from issuer {issuer} is {status}. Certificate {summary} `, values: { @@ -49,16 +55,16 @@ export const TlsTranslations = { status: '{{state.status}}', }, }), - name: i18n.translate('xpack.uptime.alerts.tls.clientName', { + name: i18n.translate('xpack.synthetics.alerts.tls.clientName', { defaultMessage: 'Uptime TLS', }), - description: i18n.translate('xpack.uptime.alerts.tls.description', { + description: i18n.translate('xpack.synthetics.alerts.tls.description', { defaultMessage: 'Alert when the TLS certificate of an Uptime monitor is about to expire.', }), }; export const TlsTranslationsLegacy = { - defaultActionMessage: i18n.translate('xpack.uptime.alerts.tls.legacy.defaultActionMessage', { + defaultActionMessage: i18n.translate('xpack.synthetics.alerts.tls.legacy.defaultActionMessage', { defaultMessage: `Detected {count} TLS certificates expiring or becoming too old. {expiringConditionalOpen} Expiring cert count: {expiringCount} @@ -81,34 +87,37 @@ Aging Certificates: {agingCommonNameAndDate} agingConditionalClose: '{{/state.hasAging}}', }, }), - name: i18n.translate('xpack.uptime.alerts.tls.legacy.clientName', { + name: i18n.translate('xpack.synthetics.alerts.tls.legacy.clientName', { defaultMessage: 'Uptime TLS (Legacy)', }), - description: i18n.translate('xpack.uptime.alerts.tls.legacy.description', { + description: i18n.translate('xpack.synthetics.alerts.tls.legacy.description', { defaultMessage: 'Alert when the TLS certificate of an Uptime monitor is about to expire. This alert will be deprecated in a future version.', }), }; export const DurationAnomalyTranslations = { - defaultActionMessage: i18n.translate('xpack.uptime.alerts.durationAnomaly.defaultActionMessage', { - defaultMessage: `Abnormal ({severity} level) response time detected on {monitor} with url {monitorUrl} at {anomalyStartTimestamp}. Anomaly severity score is {severityScore}. + defaultActionMessage: i18n.translate( + 'xpack.synthetics.alerts.durationAnomaly.defaultActionMessage', + { + defaultMessage: `Abnormal ({severity} level) response time detected on {monitor} with url {monitorUrl} at {anomalyStartTimestamp}. Anomaly severity score is {severityScore}. Response times as high as {slowestAnomalyResponse} have been detected from location {observerLocation}. Expected response time is {expectedResponseTime}.`, - values: { - severity: '{{state.severity}}', - anomalyStartTimestamp: '{{state.anomalyStartTimestamp}}', - monitor: '{{state.monitor}}', - monitorUrl: '{{{state.monitorUrl}}}', - slowestAnomalyResponse: '{{state.slowestAnomalyResponse}}', - expectedResponseTime: '{{state.expectedResponseTime}}', - severityScore: '{{state.severityScore}}', - observerLocation: '{{state.observerLocation}}', - }, - }), - name: i18n.translate('xpack.uptime.alerts.durationAnomaly.clientName', { + values: { + severity: '{{state.severity}}', + anomalyStartTimestamp: '{{state.anomalyStartTimestamp}}', + monitor: '{{state.monitor}}', + monitorUrl: '{{{state.monitorUrl}}}', + slowestAnomalyResponse: '{{state.slowestAnomalyResponse}}', + expectedResponseTime: '{{state.expectedResponseTime}}', + severityScore: '{{state.severityScore}}', + observerLocation: '{{state.observerLocation}}', + }, + } + ), + name: i18n.translate('xpack.synthetics.alerts.durationAnomaly.clientName', { defaultMessage: 'Uptime Duration Anomaly', }), - description: i18n.translate('xpack.uptime.alerts.durationAnomaly.description', { + description: i18n.translate('xpack.synthetics.alerts.durationAnomaly.description', { defaultMessage: 'Alert when the Uptime monitor duration is anomalous.', }), }; diff --git a/x-pack/plugins/synthetics/e2e/journeys/alerts/status_alert_flyouts_in_alerting_app.ts b/x-pack/plugins/synthetics/e2e/journeys/alerts/status_alert_flyouts_in_alerting_app.ts index 5346d0de33c2..321c68a8c335 100644 --- a/x-pack/plugins/synthetics/e2e/journeys/alerts/status_alert_flyouts_in_alerting_app.ts +++ b/x-pack/plugins/synthetics/e2e/journeys/alerts/status_alert_flyouts_in_alerting_app.ts @@ -27,7 +27,7 @@ journey('StatusFlyoutInAlertingApp', async ({ page, params }) => { step('Open monitor status flyout', async () => { await page.click(byTestId('createFirstRuleButton')); await waitForLoadingToFinish({ page }); - await page.click(byTestId('"xpack.uptime.alerts.monitorStatus-SelectOption"')); + await page.click(byTestId('"xpack.synthetics.alerts.monitorStatus-SelectOption"')); await waitForLoadingToFinish({ page }); await assertText({ page, text: 'This alert will apply to approximately 0 monitors.' }); }); @@ -39,9 +39,12 @@ journey('StatusFlyoutInAlertingApp', async ({ page, params }) => { }); step('can open query bar', async () => { - await page.click(byTestId('"xpack.uptime.alerts.monitorStatus.filterBar"')); + await page.click(byTestId('"xpack.synthetics.alerts.monitorStatus.filterBar"')); - await page.fill(byTestId('"xpack.uptime.alerts.monitorStatus.filterBar"'), 'monitor.type : '); + await page.fill( + byTestId('"xpack.synthetics.alerts.monitorStatus.filterBar"'), + 'monitor.type : ' + ); await waitForLoadingToFinish({ page }); @@ -58,7 +61,7 @@ journey('StatusFlyoutInAlertingApp', async ({ page, params }) => { step('Open tls alert flyout', async () => { await page.click(byTestId('createFirstRuleButton')); await waitForLoadingToFinish({ page }); - await page.click(byTestId('"xpack.uptime.alerts.tlsCertificate-SelectOption"')); + await page.click(byTestId('"xpack.synthetics.alerts.tlsCertificate-SelectOption"')); await waitForLoadingToFinish({ page }); await assertText({ page, text: 'has a certificate expiring within' }); }); diff --git a/x-pack/plugins/synthetics/e2e/journeys/alerts/tls_alert_flyouts_in_alerting_app.ts b/x-pack/plugins/synthetics/e2e/journeys/alerts/tls_alert_flyouts_in_alerting_app.ts index 873d10c0a21b..5cf1d1a6c9ff 100644 --- a/x-pack/plugins/synthetics/e2e/journeys/alerts/tls_alert_flyouts_in_alerting_app.ts +++ b/x-pack/plugins/synthetics/e2e/journeys/alerts/tls_alert_flyouts_in_alerting_app.ts @@ -27,7 +27,7 @@ journey('TlsFlyoutInAlertingApp', async ({ page, params }) => { step('Open tls alert flyout', async () => { await page.click(byTestId('createFirstRuleButton')); await waitForLoadingToFinish({ page }); - await page.click(byTestId('"xpack.uptime.alerts.tlsCertificate-SelectOption"')); + await page.click(byTestId('"xpack.synthetics.alerts.tlsCertificate-SelectOption"')); await waitForLoadingToFinish({ page }); await assertText({ page, text: 'has a certificate expiring within' }); }); diff --git a/x-pack/plugins/synthetics/e2e/journeys/monitor_details/monitor_details.journey.ts b/x-pack/plugins/synthetics/e2e/journeys/monitor_details/monitor_details.journey.ts index 2965c1acf2c2..b061942e0bb3 100644 --- a/x-pack/plugins/synthetics/e2e/journeys/monitor_details/monitor_details.journey.ts +++ b/x-pack/plugins/synthetics/e2e/journeys/monitor_details/monitor_details.journey.ts @@ -54,7 +54,7 @@ journey('MonitorDetails', async ({ page, params }: { page: Page; params: any }) 'k5tlHm0B0I9WX_CzaNxm', 'NZtkHm0B0I9WX_Cz89w9', 'zJtkHm0B0I9WX_CzftsN', - ].map((id) => page.waitForSelector(byTestId(`"xpack.uptime.pingList.ping-${id}"`))) + ].map((id) => page.waitForSelector(byTestId(`"xpack.synthetics.pingList.ping-${id}"`))) ); }); }); diff --git a/x-pack/plugins/synthetics/e2e/page_objects/monitor_details.tsx b/x-pack/plugins/synthetics/e2e/page_objects/monitor_details.tsx index 7cce2c061fa8..02395674d0cd 100644 --- a/x-pack/plugins/synthetics/e2e/page_objects/monitor_details.tsx +++ b/x-pack/plugins/synthetics/e2e/page_objects/monitor_details.tsx @@ -40,11 +40,11 @@ export function monitorDetailsPageProvider({ page, kibanaUrl }: { page: Page; ki }, async setStatusFilterUp() { - await page.click('[data-test-subj="xpack.uptime.filterBar.filterStatusUp"]'); + await page.click('[data-test-subj="xpack.synthetics.filterBar.filterStatusUp"]'); }, async setStatusFilterDown() { - await page.click('[data-test-subj="xpack.uptime.filterBar.filterStatusDown"]'); + await page.click('[data-test-subj="xpack.synthetics.filterBar.filterStatusDown"]'); }, async refreshFromES() { diff --git a/x-pack/plugins/synthetics/kibana.json b/x-pack/plugins/synthetics/kibana.json index 7eaccbbb0817..d65a89a16161 100644 --- a/x-pack/plugins/synthetics/kibana.json +++ b/x-pack/plugins/synthetics/kibana.json @@ -18,7 +18,8 @@ "share", "taskManager", "triggersActionsUi", - "usageCollection" + "usageCollection", + "unifiedSearch" ], "server": true, "ui": true, diff --git a/x-pack/plugins/synthetics/public/apps/plugin.ts b/x-pack/plugins/synthetics/public/apps/plugin.ts index ab2ae07e1c45..4a3b3668dab0 100644 --- a/x-pack/plugins/synthetics/public/apps/plugin.ts +++ b/x-pack/plugins/synthetics/public/apps/plugin.ts @@ -25,6 +25,7 @@ import { TriggersAndActionsUIPublicPluginSetup, TriggersAndActionsUIPublicPluginStart, } from '@kbn/triggers-actions-ui-plugin/public'; +import { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; import { DataPublicPluginSetup, DataPublicPluginStart } from '@kbn/data-plugin/public'; import { FleetStart } from '@kbn/fleet-plugin/public'; @@ -58,6 +59,7 @@ export interface ClientPluginsSetup { export interface ClientPluginsStart { fleet?: FleetStart; data: DataPublicPluginStart; + unifiedSearch: UnifiedSearchPublicPluginStart; discover: DiscoverStart; inspector: InspectorPluginStart; embeddable: EmbeddableStart; @@ -126,7 +128,7 @@ export class UptimePlugin sortKey: 500, entries: [ { - label: i18n.translate('xpack.uptime.overview.heading', { + label: i18n.translate('xpack.synthetics.overview.heading', { defaultMessage: 'Monitors', }), app: 'uptime', @@ -135,7 +137,7 @@ export class UptimePlugin ignoreTrailingSlash: true, }, { - label: i18n.translate('xpack.uptime.certificatesPage.heading', { + label: i18n.translate('xpack.synthetics.certificatesPage.heading', { defaultMessage: 'TLS Certificates', }), app: 'uptime', diff --git a/x-pack/plugins/synthetics/public/apps/render_app.tsx b/x-pack/plugins/synthetics/public/apps/render_app.tsx index 9a64fe9e1862..36ac093f23d0 100644 --- a/x-pack/plugins/synthetics/public/apps/render_app.tsx +++ b/x-pack/plugins/synthetics/public/apps/render_app.tsx @@ -55,7 +55,7 @@ export function renderApp( isLogsAvailable: logs, renderGlobalHelpControls: () => setHelpExtension({ - appName: i18nFormatter.translate('xpack.uptime.header.appName', { + appName: i18nFormatter.translate('xpack.synthetics.header.appName', { defaultMessage: 'Uptime', }), links: [ diff --git a/x-pack/plugins/synthetics/public/apps/uptime_app.tsx b/x-pack/plugins/synthetics/public/apps/uptime_app.tsx index b106123de1b2..1ca9573308db 100644 --- a/x-pack/plugins/synthetics/public/apps/uptime_app.tsx +++ b/x-pack/plugins/synthetics/public/apps/uptime_app.tsx @@ -84,10 +84,10 @@ const Application = (props: UptimeAppProps) => { setBadge( !canSave ? { - text: i18n.translate('xpack.uptime.badge.readOnly.text', { + text: i18n.translate('xpack.synthetics.badge.readOnly.text', { defaultMessage: 'Read only', }), - tooltip: i18n.translate('xpack.uptime.badge.readOnly.tooltip', { + tooltip: i18n.translate('xpack.synthetics.badge.readOnly.tooltip', { defaultMessage: 'Unable to save', }), iconType: 'glasses', diff --git a/x-pack/plugins/synthetics/public/apps/use_no_data_config.ts b/x-pack/plugins/synthetics/public/apps/use_no_data_config.ts index 1424d4047b6f..8898e4254874 100644 --- a/x-pack/plugins/synthetics/public/apps/use_no_data_config.ts +++ b/x-pack/plugins/synthetics/public/apps/use_no_data_config.ts @@ -25,15 +25,15 @@ export function useNoDataConfig(): KibanaPageTemplateProps['noDataConfig'] { // Returns no data config when there is no historical data if (data && !data.indexExists) { return { - solution: i18n.translate('xpack.uptime.noDataConfig.solutionName', { + solution: i18n.translate('xpack.synthetics.noDataConfig.solutionName', { defaultMessage: 'Observability', }), actions: { beats: { - title: i18n.translate('xpack.uptime.noDataConfig.beatsCard.title', { + title: i18n.translate('xpack.synthetics.noDataConfig.beatsCard.title', { defaultMessage: 'Add monitors with Heartbeat', }), - description: i18n.translate('xpack.uptime.noDataConfig.beatsCard.description', { + description: i18n.translate('xpack.synthetics.noDataConfig.beatsCard.description', { defaultMessage: 'Proactively monitor the availability of your sites and services. Receive alerts and resolve issues faster to optimize your users experience.', }), diff --git a/x-pack/plugins/synthetics/public/components/certificates/cert_status.tsx b/x-pack/plugins/synthetics/public/components/certificates/cert_status.tsx index 30bbb241205e..7ade37b33d62 100644 --- a/x-pack/plugins/synthetics/public/components/certificates/cert_status.tsx +++ b/x-pack/plugins/synthetics/public/components/certificates/cert_status.tsx @@ -86,7 +86,7 @@ export const CertStatus: React.FC = ({ cert }) => { {' '} { return ( {total ?? 0}, diff --git a/x-pack/plugins/synthetics/public/components/certificates/translations.ts b/x-pack/plugins/synthetics/public/components/certificates/translations.ts index 45ea2bf9c47c..7b7c3205cfc0 100644 --- a/x-pack/plugins/synthetics/public/components/certificates/translations.ts +++ b/x-pack/plugins/synthetics/public/components/certificates/translations.ts @@ -7,70 +7,70 @@ import { i18n } from '@kbn/i18n'; -export const OK = i18n.translate('xpack.uptime.certs.ok', { +export const OK = i18n.translate('xpack.synthetics.certs.ok', { defaultMessage: 'OK', }); -export const EXPIRED = i18n.translate('xpack.uptime.certs.expired', { +export const EXPIRED = i18n.translate('xpack.synthetics.certs.expired', { defaultMessage: 'Expired', }); -export const EXPIRES_SOON = i18n.translate('xpack.uptime.certs.expireSoon', { +export const EXPIRES_SOON = i18n.translate('xpack.synthetics.certs.expireSoon', { defaultMessage: 'Expires soon', }); -export const EXPIRES = i18n.translate('xpack.uptime.certs.expires', { +export const EXPIRES = i18n.translate('xpack.synthetics.certs.expires', { defaultMessage: 'Expires', }); -export const SEARCH_CERTS = i18n.translate('xpack.uptime.certs.searchCerts', { +export const SEARCH_CERTS = i18n.translate('xpack.synthetics.certs.searchCerts', { defaultMessage: 'Search certificates', }); -export const STATUS_COL = i18n.translate('xpack.uptime.certs.list.status', { +export const STATUS_COL = i18n.translate('xpack.synthetics.certs.list.status', { defaultMessage: 'Status', }); -export const TOO_OLD = i18n.translate('xpack.uptime.certs.list.status.old', { +export const TOO_OLD = i18n.translate('xpack.synthetics.certs.list.status.old', { defaultMessage: 'Too old', }); -export const COMMON_NAME_COL = i18n.translate('xpack.uptime.certs.list.commonName', { +export const COMMON_NAME_COL = i18n.translate('xpack.synthetics.certs.list.commonName', { defaultMessage: 'Common name', }); -export const MONITORS_COL = i18n.translate('xpack.uptime.certs.list.monitors', { +export const MONITORS_COL = i18n.translate('xpack.synthetics.certs.list.monitors', { defaultMessage: 'Monitors', }); -export const ISSUED_BY_COL = i18n.translate('xpack.uptime.certs.list.issuedBy', { +export const ISSUED_BY_COL = i18n.translate('xpack.synthetics.certs.list.issuedBy', { defaultMessage: 'Issued by', }); -export const VALID_UNTIL_COL = i18n.translate('xpack.uptime.certs.list.validUntil', { +export const VALID_UNTIL_COL = i18n.translate('xpack.synthetics.certs.list.validUntil', { defaultMessage: 'Valid until', }); -export const AGE_COL = i18n.translate('xpack.uptime.certs.list.ageCol', { +export const AGE_COL = i18n.translate('xpack.synthetics.certs.list.ageCol', { defaultMessage: 'Age', }); -export const DAYS = i18n.translate('xpack.uptime.certs.list.days', { +export const DAYS = i18n.translate('xpack.synthetics.certs.list.days', { defaultMessage: 'days', }); -export const FINGERPRINTS_COL = i18n.translate('xpack.uptime.certs.list.expirationDate', { +export const FINGERPRINTS_COL = i18n.translate('xpack.synthetics.certs.list.expirationDate', { defaultMessage: 'Fingerprints', }); -export const COPY_FINGERPRINT = i18n.translate('xpack.uptime.certs.list.copyFingerprint', { +export const COPY_FINGERPRINT = i18n.translate('xpack.synthetics.certs.list.copyFingerprint', { defaultMessage: 'Click to copy fingerprint value', }); -export const NO_CERTS_AVAILABLE = i18n.translate('xpack.uptime.certs.list.empty', { +export const NO_CERTS_AVAILABLE = i18n.translate('xpack.synthetics.certs.list.empty', { defaultMessage: 'No Certificates found. Note: Certificates are only visible for Heartbeat 7.8+', }); -export const LOADING_CERTIFICATES = i18n.translate('xpack.uptime.certificates.loading', { +export const LOADING_CERTIFICATES = i18n.translate('xpack.synthetics.certificates.loading', { defaultMessage: 'Loading certificates ...', }); diff --git a/x-pack/plugins/synthetics/public/components/common/charts/__snapshots__/donut_chart.test.tsx.snap b/x-pack/plugins/synthetics/public/components/common/charts/__snapshots__/donut_chart.test.tsx.snap index 320a64617807..19d20244e2cc 100644 --- a/x-pack/plugins/synthetics/public/components/common/charts/__snapshots__/donut_chart.test.tsx.snap +++ b/x-pack/plugins/synthetics/public/components/common/charts/__snapshots__/donut_chart.test.tsx.snap @@ -812,13 +812,13 @@ exports[`DonutChart component renders a donut chart 1`] = ` Down 32 @@ -854,13 +854,13 @@ exports[`DonutChart component renders a donut chart 1`] = ` Up 95 @@ -967,13 +967,13 @@ exports[`DonutChart component renders a green check when all monitors are up 1`] Down 0 @@ -1009,13 +1009,13 @@ exports[`DonutChart component renders a green check when all monitors are up 1`] Up 95 diff --git a/x-pack/plugins/synthetics/public/components/common/charts/annotation_tooltip.tsx b/x-pack/plugins/synthetics/public/components/common/charts/annotation_tooltip.tsx index 50a8760c10af..68269c016ab0 100644 --- a/x-pack/plugins/synthetics/public/components/common/charts/annotation_tooltip.tsx +++ b/x-pack/plugins/synthetics/public/components/common/charts/annotation_tooltip.tsx @@ -39,14 +39,14 @@ export const AnnotationTooltip = ({ details }: { details: string }) => { {moment(data.time).format('lll')}
diff --git a/x-pack/plugins/synthetics/public/components/common/charts/donut_chart.tsx b/x-pack/plugins/synthetics/public/components/common/charts/donut_chart.tsx index 3638b52e3978..457819c5448c 100644 --- a/x-pack/plugins/synthetics/public/components/common/charts/donut_chart.tsx +++ b/x-pack/plugins/synthetics/public/components/common/charts/donut_chart.tsx @@ -52,7 +52,7 @@ export const DonutChart = ({ height, down, up }: DonutChartProps) => { { const component = renderWithIntl(); expect( - component.find('[data-test-subj="xpack.uptime.snapshot.donutChart.up.label"]').text() + component.find('[data-test-subj="xpack.synthetics.snapshot.donutChart.up.label"]').text() ).toBe(STATUS_UP_LABEL); - expect(component.find('[data-test-subj="xpack.uptime.snapshot.donutChart.up"]').text()).toBe( - `${up}` - ); expect( - component.find('[data-test-subj="xpack.uptime.snapshot.donutChart.down.label"]').text() + component.find('[data-test-subj="xpack.synthetics.snapshot.donutChart.up"]').text() + ).toBe(`${up}`); + expect( + component.find('[data-test-subj="xpack.synthetics.snapshot.donutChart.down.label"]').text() ).toBe(STATUS_DOWN_LABEL); - expect(component.find('[data-test-subj="xpack.uptime.snapshot.donutChart.down"]').text()).toBe( - `${down}` - ); + expect( + component.find('[data-test-subj="xpack.synthetics.snapshot.donutChart.down"]').text() + ).toBe(`${down}`); }); }); diff --git a/x-pack/plugins/synthetics/public/components/common/charts/donut_chart_legend.tsx b/x-pack/plugins/synthetics/public/components/common/charts/donut_chart_legend.tsx index b7bb1ccab14e..f8b785730961 100644 --- a/x-pack/plugins/synthetics/public/components/common/charts/donut_chart_legend.tsx +++ b/x-pack/plugins/synthetics/public/components/common/charts/donut_chart_legend.tsx @@ -36,14 +36,14 @@ export const DonutChartLegend = ({ down, up }: Props) => { color={danger} content={down} message={STATUS_DOWN_LABEL} - data-test-subj={'xpack.uptime.snapshot.donutChart.down'} + data-test-subj={'xpack.synthetics.snapshot.donutChart.down'} /> ); diff --git a/x-pack/plugins/synthetics/public/components/common/charts/duration_chart.tsx b/x-pack/plugins/synthetics/public/components/common/charts/duration_chart.tsx index 1a09a3883557..2d82fbf1368b 100644 --- a/x-pack/plugins/synthetics/public/components/common/charts/duration_chart.tsx +++ b/x-pack/plugins/synthetics/public/components/common/charts/duration_chart.tsx @@ -95,7 +95,7 @@ export const DurationChartComponent = ({ return ( getTickFormat(d)} - title={i18n.translate('xpack.uptime.monitorCharts.durationChart.leftAxis.title', { + title={i18n.translate('xpack.synthetics.monitorCharts.durationChart.leftAxis.title', { defaultMessage: 'Duration in {unit}', values: { unit: monitor.monitor.type === 'browser' ? SECONDS_LABEL : MS_LABEL }, })} @@ -141,12 +141,12 @@ export const DurationChartComponent = ({ up }} /> } - title={i18n.translate('xpack.uptime.durationChart.emptyPrompt.title', { + title={i18n.translate('xpack.synthetics.durationChart.emptyPrompt.title', { defaultMessage: 'No duration data available', })} /> diff --git a/x-pack/plugins/synthetics/public/components/common/charts/monitor_bar_series.tsx b/x-pack/plugins/synthetics/public/components/common/charts/monitor_bar_series.tsx index 34cf55c0af48..6bb52a025154 100644 --- a/x-pack/plugins/synthetics/public/components/common/charts/monitor_bar_series.tsx +++ b/x-pack/plugins/synthetics/public/components/common/charts/monitor_bar_series.tsx @@ -92,7 +92,7 @@ export const MonitorBarSeries = ({ histogramSeries, minInterval }: MonitorBarSer id={id} color={danger} data={(histogramSeries || []).map(({ timestamp, down }) => [timestamp, down])} - name={i18n.translate('xpack.uptime.monitorList.downLineSeries.downLabel', { + name={i18n.translate('xpack.synthetics.monitorList.downLineSeries.downLabel', { defaultMessage: 'Down checks', })} timeZone="local" @@ -108,7 +108,7 @@ export const MonitorBarSeries = ({ histogramSeries, minInterval }: MonitorBarSer position="top" content={ down }} /> diff --git a/x-pack/plugins/synthetics/public/components/common/charts/ping_histogram.tsx b/x-pack/plugins/synthetics/public/components/common/charts/ping_histogram.tsx index c302f47e29bd..95b5791bced8 100644 --- a/x-pack/plugins/synthetics/public/components/common/charts/ping_histogram.tsx +++ b/x-pack/plugins/synthetics/public/components/common/charts/ping_histogram.tsx @@ -88,10 +88,10 @@ export const PingHistogramComponent: React.FC = ({ if (!data?.histogram?.length) { content = ( @@ -129,7 +129,7 @@ export const PingHistogramComponent: React.FC = ({ = ({ {...chartTheme} /> = ({ tickFormat={timeFormatter(getChartDateLabel(absoluteStartDate, absoluteEndDate))} /> numeral(d).format('0')} labelFormat={(d) => numeral(d).format('0a')} - title={i18n.translate('xpack.uptime.snapshotHistogram.yAxis.title', { + title={i18n.translate('xpack.synthetics.snapshotHistogram.yAxis.title', { defaultMessage: 'Pings', description: 'The label on the y-axis of a chart that displays the number of times Heartbeat has pinged a set of services/websites.', @@ -176,7 +176,7 @@ export const PingHistogramComponent: React.FC = ({ color={[danger, gray]} data={barData} id={STATUS_DOWN_LABEL} - name={i18n.translate('xpack.uptime.snapshotHistogram.series.pings', { + name={i18n.translate('xpack.synthetics.snapshotHistogram.series.pings', { defaultMessage: 'Monitor Pings', })} stackAccessors={['x']} @@ -222,7 +222,7 @@ export const PingHistogramComponent: React.FC = ({

@@ -231,7 +231,10 @@ export const PingHistogramComponent: React.FC = ({ {showAnalyzeButton && ( - + )} diff --git a/x-pack/plugins/synthetics/public/components/common/header/action_menu_content.tsx b/x-pack/plugins/synthetics/public/components/common/header/action_menu_content.tsx index 3e7a6db48ff3..311c76c87a84 100644 --- a/x-pack/plugins/synthetics/public/components/common/header/action_menu_content.tsx +++ b/x-pack/plugins/synthetics/public/components/common/header/action_menu_content.tsx @@ -22,15 +22,15 @@ import { InspectorHeaderLink } from './inspector_header_link'; import { monitorStatusSelector } from '../../../state/selectors'; import { ManageMonitorsBtn } from './manage_monitors_btn'; -const ADD_DATA_LABEL = i18n.translate('xpack.uptime.addDataButtonLabel', { +const ADD_DATA_LABEL = i18n.translate('xpack.synthetics.addDataButtonLabel', { defaultMessage: 'Add data', }); -const ANALYZE_DATA = i18n.translate('xpack.uptime.analyzeDataButtonLabel', { +const ANALYZE_DATA = i18n.translate('xpack.synthetics.analyzeDataButtonLabel', { defaultMessage: 'Explore data', }); -const ANALYZE_MESSAGE = i18n.translate('xpack.uptime.analyzeDataButtonLabel.message', { +const ANALYZE_MESSAGE = i18n.translate('xpack.synthetics.analyzeDataButtonLabel.message', { defaultMessage: 'Explore Data allows you to select and filter result data in any dimension and look for the cause or impact of performance problems.', }); @@ -76,7 +76,7 @@ export function ActionMenuContent(): React.ReactElement { - + {ANALYZE_MESSAGE}

}> - {i18n.translate('xpack.uptime.inspectButtonText', { + {i18n.translate('xpack.synthetics.inspectButtonText', { defaultMessage: 'Inspect', })} diff --git a/x-pack/plugins/synthetics/public/components/common/header/manage_monitors_btn.tsx b/x-pack/plugins/synthetics/public/components/common/header/manage_monitors_btn.tsx index 5cc489fe4d7a..0e7215c0f5a0 100644 --- a/x-pack/plugins/synthetics/public/components/common/header/manage_monitors_btn.tsx +++ b/x-pack/plugins/synthetics/public/components/common/header/manage_monitors_btn.tsx @@ -19,7 +19,7 @@ import { ClientPluginsSetup } from '../../../apps/plugin'; import { useUptimeSettingsContext } from '../../../contexts/uptime_settings_context'; export const ManageMonitorsBtn = () => { - const [isOpen, setIsOpen] = useLocalStorage('xpack.uptime.monitorManagement.openTour', true); + const [isOpen, setIsOpen] = useLocalStorage('xpack.synthetics.monitorManagement.openTour', true); const history = useHistory(); @@ -70,7 +70,7 @@ export const ManageMonitorsBtn = () => { onClick={handleOnClick} > @@ -79,23 +79,23 @@ export const ManageMonitorsBtn = () => { }; const GETTING_STARTED_LABEL = i18n.translate( - 'xpack.uptime.monitorManagement.gettingStarted.label', + 'xpack.synthetics.monitorManagement.gettingStarted.label', { defaultMessage: 'Get started with Synthetic Monitoring', } ); -const MONITOR_MANAGEMENT_LABEL = i18n.translate('xpack.uptime.monitorManagement.try.label', { +const MONITOR_MANAGEMENT_LABEL = i18n.translate('xpack.synthetics.monitorManagement.try.label', { defaultMessage: 'Try Monitor Management', }); -const DISMISS_LABEL = i18n.translate('xpack.uptime.monitorManagement.try.dismiss', { +const DISMISS_LABEL = i18n.translate('xpack.synthetics.monitorManagement.try.dismiss', { defaultMessage: 'Dismiss', }); -const NAVIGATE_LABEL = i18n.translate('xpack.uptime.page_header.manageLink.label', { +const NAVIGATE_LABEL = i18n.translate('xpack.synthetics.page_header.manageLink.label', { defaultMessage: 'Navigate to the Uptime Monitor Management page', }); -const NEW_LABEL = i18n.translate('xpack.uptime.monitorManagement.new.label', { +const NEW_LABEL = i18n.translate('xpack.synthetics.monitorManagement.new.label', { defaultMessage: 'New', }); diff --git a/x-pack/plugins/synthetics/public/components/common/header/page_tabs.tsx b/x-pack/plugins/synthetics/public/components/common/header/page_tabs.tsx index 74b037971c12..1210dbb0f229 100644 --- a/x-pack/plugins/synthetics/public/components/common/header/page_tabs.tsx +++ b/x-pack/plugins/synthetics/public/components/common/header/page_tabs.tsx @@ -17,7 +17,7 @@ import { stringifyUrlParams } from '../../../lib/helper/stringify_url_params'; const tabs = [ { id: OVERVIEW_ROUTE, - name: i18n.translate('xpack.uptime.overviewPage.headerText', { + name: i18n.translate('xpack.synthetics.overviewPage.headerText', { defaultMessage: 'Overview', description: `The text that will be displayed in the app's heading when the Overview page loads.`, }), diff --git a/x-pack/plugins/synthetics/public/components/common/location_link.tsx b/x-pack/plugins/synthetics/public/components/common/location_link.tsx index 7e2ee54008e9..9bf66ab24360 100644 --- a/x-pack/plugins/synthetics/public/components/common/location_link.tsx +++ b/x-pack/plugins/synthetics/public/components/common/location_link.tsx @@ -28,7 +28,7 @@ export const LocationLink = ({ location, textSize }: LocationLinkProps) => { ) : ( - {i18n.translate('xpack.uptime.monitorList.geoName.helpLinkAnnotation', { + {i18n.translate('xpack.synthetics.monitorList.geoName.helpLinkAnnotation', { defaultMessage: 'Add location', description: 'Text that instructs the user to navigate to our docs to add a geographic location to their data', diff --git a/x-pack/plugins/synthetics/public/components/common/monitor_tags.tsx b/x-pack/plugins/synthetics/public/components/common/monitor_tags.tsx index 35684e8326f6..e8e8a61e7b30 100644 --- a/x-pack/plugins/synthetics/public/components/common/monitor_tags.tsx +++ b/x-pack/plugins/synthetics/public/components/common/monitor_tags.tsx @@ -37,7 +37,7 @@ const getTagsFromPing = (ping: Ping) => { }; const getFilterLabel = (tag: string) => { - return i18n.translate('xpack.uptime.monitorList.tags.filter', { + return i18n.translate('xpack.synthetics.monitorList.tags.filter', { defaultMessage: 'Filter all monitors with tag {tag}', values: { tag, diff --git a/x-pack/plugins/synthetics/public/components/common/translations.ts b/x-pack/plugins/synthetics/public/components/common/translations.ts index db772d8bed9d..8587ddd6f863 100644 --- a/x-pack/plugins/synthetics/public/components/common/translations.ts +++ b/x-pack/plugins/synthetics/public/components/common/translations.ts @@ -7,44 +7,47 @@ import { i18n } from '@kbn/i18n'; -export const URL_LABEL = i18n.translate('xpack.uptime.monitorList.table.url.name', { +export const URL_LABEL = i18n.translate('xpack.synthetics.monitorList.table.url.name', { defaultMessage: 'Url', }); -export const TAGS_LABEL = i18n.translate('xpack.uptime.monitorList.table.tags.name', { +export const TAGS_LABEL = i18n.translate('xpack.synthetics.monitorList.table.tags.name', { defaultMessage: 'Tags', }); -export const STATUS_UP_LABEL = i18n.translate('xpack.uptime.monitorList.statusColumn.upLabel', { +export const STATUS_UP_LABEL = i18n.translate('xpack.synthetics.monitorList.statusColumn.upLabel', { defaultMessage: 'Up', }); -export const STATUS_DOWN_LABEL = i18n.translate('xpack.uptime.monitorList.statusColumn.downLabel', { - defaultMessage: 'Down', -}); +export const STATUS_DOWN_LABEL = i18n.translate( + 'xpack.synthetics.monitorList.statusColumn.downLabel', + { + defaultMessage: 'Down', + } +); export const STATUS_COMPLETE_LABEL = i18n.translate( - 'xpack.uptime.monitorList.statusColumn.completeLabel', + 'xpack.synthetics.monitorList.statusColumn.completeLabel', { defaultMessage: 'Complete', } ); export const STATUS_FAILED_LABEL = i18n.translate( - 'xpack.uptime.monitorList.statusColumn.failedLabel', + 'xpack.synthetics.monitorList.statusColumn.failedLabel', { defaultMessage: 'Failed', } ); -export const SECONDS_LABEL = i18n.translate('xpack.uptime.seconds.label', { +export const SECONDS_LABEL = i18n.translate('xpack.synthetics.seconds.label', { defaultMessage: 'seconds', }); -export const SEC_LABEL = i18n.translate('xpack.uptime.seconds.shortForm.label', { +export const SEC_LABEL = i18n.translate('xpack.synthetics.seconds.shortForm.label', { defaultMessage: 'sec', }); -export const MS_LABEL = i18n.translate('xpack.uptime.millisecond.abbreviation.label', { +export const MS_LABEL = i18n.translate('xpack.synthetics.millisecond.abbreviation.label', { defaultMessage: 'ms', }); diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/browser/advanced_fields.tsx b/x-pack/plugins/synthetics/public/components/fleet_package/browser/advanced_fields.tsx index 5db5aabd791e..64409608f7a4 100644 --- a/x-pack/plugins/synthetics/public/components/fleet_package/browser/advanced_fields.tsx +++ b/x-pack/plugins/synthetics/public/components/fleet_package/browser/advanced_fields.tsx @@ -57,14 +57,14 @@ export const BrowserAdvancedFields = memo( title={

} description={ } @@ -73,14 +73,14 @@ export const BrowserAdvancedFields = memo( } labelAppend={} helpText={ } @@ -100,14 +100,14 @@ export const BrowserAdvancedFields = memo( } labelAppend={} helpText={ } @@ -128,14 +128,14 @@ export const BrowserAdvancedFields = memo( title={

} description={ } @@ -145,7 +145,7 @@ export const BrowserAdvancedFields = memo( helpText={ <> @@ -157,7 +157,7 @@ export const BrowserAdvancedFields = memo( checked={fields[ConfigKey.IGNORE_HTTPS_ERRORS]} label={ } @@ -174,14 +174,14 @@ export const BrowserAdvancedFields = memo( } labelAppend={} helpText={ } @@ -202,14 +202,14 @@ export const BrowserAdvancedFields = memo( } labelAppend={} helpText={ } diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/browser/script_recorder_fields.tsx b/x-pack/plugins/synthetics/public/components/fleet_package/browser/script_recorder_fields.tsx index e85bebe7f6b8..704fbd6edcdf 100644 --- a/x-pack/plugins/synthetics/public/components/fleet_package/browser/script_recorder_fields.tsx +++ b/x-pack/plugins/synthetics/public/components/fleet_package/browser/script_recorder_fields.tsx @@ -46,7 +46,7 @@ export function ScriptRecorderFields({ onChange, script, fileName }: Props) { @@ -71,7 +71,7 @@ export function ScriptRecorderFields({ onChange, script, fileName }: Props) { iconSide="right" > @@ -85,7 +85,7 @@ export function ScriptRecorderFields({ onChange, script, fileName }: Props) { color="danger" > @@ -120,14 +120,14 @@ export function ScriptRecorderFields({ onChange, script, fileName }: Props) { } const PLACEHOLDER_FILE_NAME = i18n.translate( - 'xpack.uptime.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.scriptRecorder.mockFileName', + 'xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.scriptRecorder.mockFileName', { defaultMessage: 'test_script.js', } ); const CLOSE_BUTTON_LABEL = i18n.translate( - 'xpack.uptime.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.scriptRecorder.closeButtonLabel', + 'xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.scriptRecorder.closeButtonLabel', { defaultMessage: 'Close script flyout', } diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/browser/simple_fields.tsx b/x-pack/plugins/synthetics/public/components/fleet_package/browser/simple_fields.tsx index ddf8f5699b5f..e25f135c5efa 100644 --- a/x-pack/plugins/synthetics/public/components/fleet_package/browser/simple_fields.tsx +++ b/x-pack/plugins/synthetics/public/components/fleet_package/browser/simple_fields.tsx @@ -72,14 +72,14 @@ export const BrowserSimpleFields = memo(({ validate, onFieldBlur }) => { id="syntheticsFleetScheduleField--number syntheticsFleetScheduleField--unit" label={ } isInvalid={!!validate[ConfigKey.SCHEDULE]?.(fields)} error={ } @@ -99,7 +99,7 @@ export const BrowserSimpleFields = memo(({ validate, onFieldBlur }) => { } diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/browser/source_field.tsx b/x-pack/plugins/synthetics/public/components/fleet_package/browser/source_field.tsx index 89f346180092..1da198c17eb5 100644 --- a/x-pack/plugins/synthetics/public/components/fleet_package/browser/source_field.tsx +++ b/x-pack/plugins/synthetics/public/components/fleet_package/browser/source_field.tsx @@ -85,7 +85,7 @@ export const SourceField = ({ onChange, onFieldBlur, defaultConfig = defaultValu const zipUrlLabel = ( ); @@ -104,13 +104,13 @@ export const SourceField = ({ onChange, onFieldBlur, defaultConfig = defaultValu isInvalid={!config.zipUrl} error={ } helpText={ } @@ -128,14 +128,14 @@ export const SourceField = ({ onChange, onFieldBlur, defaultConfig = defaultValu } labelAppend={} helpText={ } @@ -152,14 +152,14 @@ export const SourceField = ({ onChange, onFieldBlur, defaultConfig = defaultValu } labelAppend={} helpText={ } @@ -176,21 +176,21 @@ export const SourceField = ({ onChange, onFieldBlur, defaultConfig = defaultValu } labelAppend={} helpText={ } > } labelAppend={} helpText={ } @@ -232,14 +232,14 @@ export const SourceField = ({ onChange, onFieldBlur, defaultConfig = defaultValu } labelAppend={} helpText={ } @@ -260,7 +260,7 @@ export const SourceField = ({ onChange, onFieldBlur, defaultConfig = defaultValu id: 'syntheticsBrowserInlineConfig', name: ( ), @@ -270,20 +270,20 @@ export const SourceField = ({ onChange, onFieldBlur, defaultConfig = defaultValu isInvalid={!config.inlineScript} error={ } helpText={ } > { } @@ -46,7 +46,7 @@ export const ThrottlingDisabledCallout = () => { iconType="alert" > @@ -58,7 +58,7 @@ export const ThrottlingExceededCallout = () => { } @@ -66,7 +66,7 @@ export const ThrottlingExceededCallout = () => { iconType="alert" > @@ -82,7 +82,7 @@ export const ThrottlingExceededMessage = ({ }) => { return ( @@ -115,7 +115,7 @@ export const ThrottlingFields = memo(({ validate, minColumnWidth, onField } @@ -126,7 +126,7 @@ export const ThrottlingFields = memo(({ validate, minColumnWidth, onField ) : ( ) @@ -154,7 +154,7 @@ export const ThrottlingFields = memo(({ validate, minColumnWidth, onField } @@ -165,7 +165,7 @@ export const ThrottlingFields = memo(({ validate, minColumnWidth, onField ) : ( ) @@ -193,7 +193,7 @@ export const ThrottlingFields = memo(({ validate, minColumnWidth, onField } @@ -201,7 +201,7 @@ export const ThrottlingFields = memo(({ validate, minColumnWidth, onField isInvalid={!!validate[ConfigKey.LATENCY]?.(fields)} error={ } @@ -238,14 +238,14 @@ export const ThrottlingFields = memo(({ validate, minColumnWidth, onField title={

} description={ } @@ -257,7 +257,7 @@ export const ThrottlingFields = memo(({ validate, minColumnWidth, onField checked={fields[ConfigKey.IS_THROTTLING_ENABLED]} label={ } diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/browser/uploader.tsx b/x-pack/plugins/synthetics/public/components/fleet_package/browser/uploader.tsx index 76c2287ca645..ce65818d23fc 100644 --- a/x-pack/plugins/synthetics/public/components/fleet_package/browser/uploader.tsx +++ b/x-pack/plugins/synthetics/public/components/fleet_package/browser/uploader.tsx @@ -63,21 +63,21 @@ export function Uploader({ onUpload }: Props) { } const TESTING_SCRIPT_LABEL = i18n.translate( - 'xpack.uptime.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.uploader.fieldLabel', + 'xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.uploader.fieldLabel', { defaultMessage: 'Testing script', } ); const PROMPT_TEXT = i18n.translate( - 'xpack.uptime.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.uploader.label', + 'xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.uploader.label', { defaultMessage: 'Select recorder-generated .js file', } ); const INVALID_FILE_ERROR = i18n.translate( - 'xpack.uptime.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.uploader.invalidFileError', + 'xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.uploader.invalidFileError', { defaultMessage: 'Invalid file type. Please upload a .js file generated by the Elastic Synthetics Recorder.', @@ -85,7 +85,7 @@ const INVALID_FILE_ERROR = i18n.translate( ); const PARSING_ERROR = i18n.translate( - 'xpack.uptime.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.uploader.parsingError', + 'xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.uploader.parsingError', { defaultMessage: 'Error uploading file. Please upload a .js file generated by the Elastic Synthetics Recorder in inline script format.', diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/browser/zip_url_tls_fields.tsx b/x-pack/plugins/synthetics/public/components/fleet_package/browser/zip_url_tls_fields.tsx index 590c5d4f3abc..21f2a548413c 100644 --- a/x-pack/plugins/synthetics/public/components/fleet_package/browser/zip_url_tls_fields.tsx +++ b/x-pack/plugins/synthetics/public/components/fleet_package/browser/zip_url_tls_fields.tsx @@ -62,7 +62,7 @@ export const ZipUrlTLSFields = () => { checked={!!isZipUrlTLSEnabled} label={ } diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/common/common_fields.tsx b/x-pack/plugins/synthetics/public/components/fleet_package/common/common_fields.tsx index 78569e8ba65a..acdc6655cf24 100644 --- a/x-pack/plugins/synthetics/public/components/fleet_package/common/common_fields.tsx +++ b/x-pack/plugins/synthetics/public/components/fleet_package/common/common_fields.tsx @@ -45,14 +45,14 @@ export function CommonFields({ fields, onChange, onFieldBlur, validate }: Props) } labelAppend={} helpText={ } @@ -73,7 +73,7 @@ export function CommonFields({ fields, onChange, onFieldBlur, validate }: Props) } @@ -81,19 +81,19 @@ export function CommonFields({ fields, onChange, onFieldBlur, validate }: Props) error={ parseInt(fields[ConfigKey.TIMEOUT] || '', 10) < 0 ? ( ) : ( ) } helpText={ } @@ -115,14 +115,14 @@ export function CommonFields({ fields, onChange, onFieldBlur, validate }: Props) } labelAppend={} helpText={ } diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/common/enabled.tsx b/x-pack/plugins/synthetics/public/components/fleet_package/common/enabled.tsx index 29f947984b34..1d76fe8a72e8 100644 --- a/x-pack/plugins/synthetics/public/components/fleet_package/common/enabled.tsx +++ b/x-pack/plugins/synthetics/public/components/fleet_package/common/enabled.tsx @@ -22,7 +22,7 @@ export function Enabled({ fields, onChange, onBlur }: Props) { } @@ -30,7 +30,7 @@ export function Enabled({ fields, onChange, onBlur }: Props) { } diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/common/tls_options.tsx b/x-pack/plugins/synthetics/public/components/fleet_package/common/tls_options.tsx index 76b49c04ad2c..3103a6b90912 100644 --- a/x-pack/plugins/synthetics/public/components/fleet_package/common/tls_options.tsx +++ b/x-pack/plugins/synthetics/public/components/fleet_package/common/tls_options.tsx @@ -97,7 +97,7 @@ export const TLSOptions: React.FunctionComponent = memo( @@ -108,7 +108,7 @@ export const TLSOptions: React.FunctionComponent = memo( } @@ -135,7 +135,7 @@ export const TLSOptions: React.FunctionComponent = memo( } @@ -144,7 +144,7 @@ export const TLSOptions: React.FunctionComponent = memo( >

@@ -156,7 +156,7 @@ export const TLSOptions: React.FunctionComponent = memo( } @@ -165,7 +165,7 @@ export const TLSOptions: React.FunctionComponent = memo( > = memo( } helpText={ } @@ -219,14 +219,14 @@ export const TLSOptions: React.FunctionComponent = memo( <> {tlsRoleLabels[tlsRole]}{' '} } helpText={ } @@ -256,14 +256,14 @@ export const TLSOptions: React.FunctionComponent = memo( <> {tlsRoleLabels[tlsRole]}{' '} } helpText={ } @@ -293,14 +293,14 @@ export const TLSOptions: React.FunctionComponent = memo( <> {tlsRoleLabels[tlsRole]}{' '} } helpText={ } @@ -326,13 +326,13 @@ export const TLSOptions: React.FunctionComponent = memo( const tlsRoleLabels = { client: ( ), server: ( ), @@ -340,28 +340,28 @@ const tlsRoleLabels = { const verificationModeHelpText = { [VerificationMode.CERTIFICATE]: i18n.translate( - 'xpack.uptime.createPackagePolicy.stepConfigure.certsField.verificationMode.certificate.description', + 'xpack.synthetics.createPackagePolicy.stepConfigure.certsField.verificationMode.certificate.description', { defaultMessage: 'Verifies that the provided certificate is signed by a trusted authority (CA), but does not perform any hostname verification.', } ), [VerificationMode.FULL]: i18n.translate( - 'xpack.uptime.createPackagePolicy.stepConfigure.certsField.verificationMode.full.description', + 'xpack.synthetics.createPackagePolicy.stepConfigure.certsField.verificationMode.full.description', { defaultMessage: 'Verifies that the provided certificate is signed by a trusted authority (CA) and also verifies that the server’s hostname (or IP address) matches the names identified within the certificate.', } ), [VerificationMode.NONE]: i18n.translate( - 'xpack.uptime.createPackagePolicy.stepConfigure.certsField.verificationMode.none.description', + 'xpack.synthetics.createPackagePolicy.stepConfigure.certsField.verificationMode.none.description', { defaultMessage: 'Performs no verification of the server’s certificate. It is primarily intended as a temporary diagnostic mechanism when attempting to resolve TLS errors; its use in production environments is strongly discouraged.', } ), [VerificationMode.STRICT]: i18n.translate( - 'xpack.uptime.createPackagePolicy.stepConfigure.certsField.verificationMode.strict.description', + 'xpack.synthetics.createPackagePolicy.stepConfigure.certsField.verificationMode.strict.description', { defaultMessage: 'Verifies that the provided certificate is signed by a trusted authority (CA) and also verifies that the server’s hostname (or IP address) matches the names identified within the certificate. If the Subject Alternative Name is empty, it returns an error.', @@ -371,25 +371,25 @@ const verificationModeHelpText = { const verificationModeLabels = { [VerificationMode.CERTIFICATE]: i18n.translate( - 'xpack.uptime.createPackagePolicy.stepConfigure.certsField.verificationMode.certificate.label', + 'xpack.synthetics.createPackagePolicy.stepConfigure.certsField.verificationMode.certificate.label', { defaultMessage: 'Certificate', } ), [VerificationMode.FULL]: i18n.translate( - 'xpack.uptime.createPackagePolicy.stepConfigure.certsField.verificationMode.full.label', + 'xpack.synthetics.createPackagePolicy.stepConfigure.certsField.verificationMode.full.label', { defaultMessage: 'Full', } ), [VerificationMode.NONE]: i18n.translate( - 'xpack.uptime.createPackagePolicy.stepConfigure.certsField.verificationMode.none.label', + 'xpack.synthetics.createPackagePolicy.stepConfigure.certsField.verificationMode.none.label', { defaultMessage: 'None', } ), [VerificationMode.STRICT]: i18n.translate( - 'xpack.uptime.createPackagePolicy.stepConfigure.certsField.verificationMode.strict.label', + 'xpack.synthetics.createPackagePolicy.stepConfigure.certsField.verificationMode.strict.label', { defaultMessage: 'Strict', } diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/custom_fields.tsx b/x-pack/plugins/synthetics/public/components/fleet_package/custom_fields.tsx index f1e4210f206d..928c11249f69 100644 --- a/x-pack/plugins/synthetics/public/components/fleet_package/custom_fields.tsx +++ b/x-pack/plugins/synthetics/public/components/fleet_package/custom_fields.tsx @@ -47,7 +47,7 @@ const dataStreamToString = [ { value: DataStream.BROWSER, text: i18n.translate( - 'xpack.uptime.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browserLabel', + 'xpack.synthetics.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browserLabel', { defaultMessage: 'Browser (Beta)', } @@ -104,14 +104,14 @@ export const CustomFields = memo( title={

} description={ } @@ -124,7 +124,7 @@ export const CustomFields = memo( } @@ -135,7 +135,7 @@ export const CustomFields = memo( } error={ } @@ -154,7 +154,7 @@ export const CustomFields = memo( ( external >
@@ -187,14 +187,14 @@ export const CustomFields = memo( title={

} description={ } @@ -206,7 +206,7 @@ export const CustomFields = memo( checked={!!isTLSEnabled} label={ } diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/header_field.tsx b/x-pack/plugins/synthetics/public/components/fleet_package/header_field.tsx index c80ed6cd300f..112a0879d404 100644 --- a/x-pack/plugins/synthetics/public/components/fleet_package/header_field.tsx +++ b/x-pack/plugins/synthetics/public/components/fleet_package/header_field.tsx @@ -57,7 +57,7 @@ export const HeaderField = ({ } diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/http/advanced_fields.tsx b/x-pack/plugins/synthetics/public/components/fleet_package/http/advanced_fields.tsx index c9396ac69fba..e2afb91e3f68 100644 --- a/x-pack/plugins/synthetics/public/components/fleet_package/http/advanced_fields.tsx +++ b/x-pack/plugins/synthetics/public/components/fleet_package/http/advanced_fields.tsx @@ -52,7 +52,7 @@ export const HTTPAdvancedFields = memo( id="uptimeFleetHttpAdvancedOptions" buttonContent={ } @@ -64,14 +64,14 @@ export const HTTPAdvancedFields = memo( title={

} description={ } @@ -81,14 +81,14 @@ export const HTTPAdvancedFields = memo( } labelAppend={} helpText={ } @@ -108,14 +108,14 @@ export const HTTPAdvancedFields = memo( } labelAppend={} helpText={ } @@ -135,14 +135,14 @@ export const HTTPAdvancedFields = memo( } labelAppend={} helpText={ } @@ -162,13 +162,13 @@ export const HTTPAdvancedFields = memo( } helpText={ } @@ -190,7 +190,7 @@ export const HTTPAdvancedFields = memo( fullWidth label={ } @@ -198,13 +198,13 @@ export const HTTPAdvancedFields = memo( isInvalid={!!validate[ConfigKey.REQUEST_HEADERS_CHECK]?.(fields)} error={ } helpText={ } @@ -231,14 +231,14 @@ export const HTTPAdvancedFields = memo( } labelAppend={} helpText={ } @@ -265,14 +265,14 @@ export const HTTPAdvancedFields = memo( title={

} description={ } @@ -282,7 +282,7 @@ export const HTTPAdvancedFields = memo( helpText={ <> http.response.body.headers @@ -295,7 +295,7 @@ export const HTTPAdvancedFields = memo( checked={fields[ConfigKey.RESPONSE_HEADERS_INDEX]} label={ } @@ -312,7 +312,7 @@ export const HTTPAdvancedFields = memo( helpText={ <> http.response.body.contents @@ -335,14 +335,14 @@ export const HTTPAdvancedFields = memo( title={

} description={ } @@ -350,7 +350,7 @@ export const HTTPAdvancedFields = memo( } @@ -358,12 +358,12 @@ export const HTTPAdvancedFields = memo( isInvalid={!!validate[ConfigKey.RESPONSE_STATUS_CHECK]?.(fields)} error={ } helpText={i18n.translate( - 'xpack.uptime.createPackagePolicy.stepConfigure.httpAdvancedOptions.responseChecks.responseStatusCheck.helpText', + 'xpack.synthetics.createPackagePolicy.stepConfigure.httpAdvancedOptions.responseChecks.responseStatusCheck.helpText', { defaultMessage: 'A list of expected status codes. Press enter to add a new code. 4xx and 5xx codes are considered down by default. Other codes are considered up.', @@ -386,7 +386,7 @@ export const HTTPAdvancedFields = memo( fullWidth label={ } @@ -394,13 +394,13 @@ export const HTTPAdvancedFields = memo( isInvalid={!!validate[ConfigKey.RESPONSE_HEADERS_CHECK]?.(fields)} error={[ , ]} helpText={ } @@ -422,13 +422,13 @@ export const HTTPAdvancedFields = memo( } labelAppend={} helpText={i18n.translate( - 'xpack.uptime.createPackagePolicy.stepConfigure.httpAdvancedOptions.responseBodyCheckPositive.helpText', + 'xpack.synthetics.createPackagePolicy.stepConfigure.httpAdvancedOptions.responseBodyCheckPositive.helpText', { defaultMessage: 'A list of regular expressions to match the body output. Press enter to add a new expression. Only a single expression needs to match.', @@ -452,13 +452,13 @@ export const HTTPAdvancedFields = memo( } labelAppend={} helpText={i18n.translate( - 'xpack.uptime.createPackagePolicy.stepConfigure.httpAdvancedOptions.responseBodyCheckNegative.helpText', + 'xpack.synthetics.createPackagePolicy.stepConfigure.httpAdvancedOptions.responseBodyCheckNegative.helpText', { defaultMessage: 'A list of regular expressions to match the the body output negatively. Press enter to add a new expression. Return match failed if single expression matches.', diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/http/simple_fields.tsx b/x-pack/plugins/synthetics/public/components/fleet_package/http/simple_fields.tsx index 56245856f5ae..9e7f2f4bab6a 100644 --- a/x-pack/plugins/synthetics/public/components/fleet_package/http/simple_fields.tsx +++ b/x-pack/plugins/synthetics/public/components/fleet_package/http/simple_fields.tsx @@ -38,14 +38,14 @@ export const HTTPSimpleFields = memo(({ validate, onFieldBlur }) => { } isInvalid={!!validate[ConfigKey.URLS]?.(fields)} error={ } @@ -63,14 +63,14 @@ export const HTTPSimpleFields = memo(({ validate, onFieldBlur }) => { id="syntheticsFleetScheduleField" label={ } isInvalid={!!validate[ConfigKey.SCHEDULE]?.(fields)} error={ } @@ -90,21 +90,21 @@ export const HTTPSimpleFields = memo(({ validate, onFieldBlur }) => { } isInvalid={!!validate[ConfigKey.MAX_REDIRECTS]?.(fields)} error={ } labelAppend={} helpText={ } diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/icmp/advanced_fields.tsx b/x-pack/plugins/synthetics/public/components/fleet_package/icmp/advanced_fields.tsx index ed08443b2a9f..7aac2de3cf14 100644 --- a/x-pack/plugins/synthetics/public/components/fleet_package/icmp/advanced_fields.tsx +++ b/x-pack/plugins/synthetics/public/components/fleet_package/icmp/advanced_fields.tsx @@ -16,7 +16,7 @@ export const ICMPAdvancedFields = ({ children }: { children?: React.ReactNode }) id="uptimeFleetIcmpAdvancedOptions" buttonContent={ } diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/icmp/simple_fields.tsx b/x-pack/plugins/synthetics/public/components/fleet_package/icmp/simple_fields.tsx index 76058d5cc324..c420fce0176b 100644 --- a/x-pack/plugins/synthetics/public/components/fleet_package/icmp/simple_fields.tsx +++ b/x-pack/plugins/synthetics/public/components/fleet_package/icmp/simple_fields.tsx @@ -38,14 +38,14 @@ export const ICMPSimpleFields = memo(({ validate, onFieldBlur }) => { } isInvalid={!!validate[ConfigKey.HOSTS]?.(fields)} error={ } @@ -66,14 +66,14 @@ export const ICMPSimpleFields = memo(({ validate, onFieldBlur }) => { id="syntheticsFleetScheduleField--number syntheticsFleetScheduleField--unit" label={ } isInvalid={!!validate[ConfigKey.SCHEDULE]?.(fields)} error={ } @@ -93,21 +93,21 @@ export const ICMPSimpleFields = memo(({ validate, onFieldBlur }) => { } isInvalid={!!validate[ConfigKey.WAIT]?.(fields)} error={ } labelAppend={} helpText={ } diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/index_response_body_field.tsx b/x-pack/plugins/synthetics/public/components/fleet_package/index_response_body_field.tsx index b8b350b84d10..aff500151e68 100644 --- a/x-pack/plugins/synthetics/public/components/fleet_package/index_response_body_field.tsx +++ b/x-pack/plugins/synthetics/public/components/fleet_package/index_response_body_field.tsx @@ -45,7 +45,7 @@ export const ResponseBodyIndexField = ({ defaultValue, onChange, onBlur }: Props checked={checked} label={ } @@ -60,7 +60,7 @@ export const ResponseBodyIndexField = ({ defaultValue, onChange, onBlur }: Props { } @@ -128,7 +128,7 @@ export const KeyValuePairsField = ({ { } @@ -151,7 +151,7 @@ export const KeyValuePairsField = ({ handleOnChange(event, index, true)} @@ -174,9 +177,12 @@ export const KeyValuePairsField = ({ } endControl={ handleOnChange(event, index, false)} diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/optional_label.tsx b/x-pack/plugins/synthetics/public/components/fleet_package/optional_label.tsx index 8d81b77b01fa..a9db178a84bb 100644 --- a/x-pack/plugins/synthetics/public/components/fleet_package/optional_label.tsx +++ b/x-pack/plugins/synthetics/public/components/fleet_package/optional_label.tsx @@ -12,7 +12,7 @@ export const OptionalLabel = () => { return ( diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/request_body_field.tsx b/x-pack/plugins/synthetics/public/components/fleet_package/request_body_field.tsx index a81a4b076ad7..19cb8cda978f 100644 --- a/x-pack/plugins/synthetics/public/components/fleet_package/request_body_field.tsx +++ b/x-pack/plugins/synthetics/public/components/fleet_package/request_body_field.tsx @@ -87,7 +87,7 @@ export const RequestBodyField = ({ onChange, onBlur, type, value }: Props) => { content: ( { content: ( { content: ( { } @@ -180,24 +180,27 @@ export const RequestBodyField = ({ onChange, onBlur, type, value }: Props) => { const modeLabels = { [Mode.FORM]: i18n.translate( - 'xpack.uptime.createPackagePolicy.stepConfigure.requestBodyType.form', + 'xpack.synthetics.createPackagePolicy.stepConfigure.requestBodyType.form', { defaultMessage: 'Form', } ), [Mode.PLAINTEXT]: i18n.translate( - 'xpack.uptime.createPackagePolicy.stepConfigure.requestBodyType.text', + 'xpack.synthetics.createPackagePolicy.stepConfigure.requestBodyType.text', { defaultMessage: 'Text', } ), [Mode.JSON]: i18n.translate( - 'xpack.uptime.createPackagePolicy.stepConfigure.requestBodyType.JSON', + 'xpack.synthetics.createPackagePolicy.stepConfigure.requestBodyType.JSON', { defaultMessage: 'JSON', } ), - [Mode.XML]: i18n.translate('xpack.uptime.createPackagePolicy.stepConfigure.requestBodyType.XML', { - defaultMessage: 'XML', - }), + [Mode.XML]: i18n.translate( + 'xpack.synthetics.createPackagePolicy.stepConfigure.requestBodyType.XML', + { + defaultMessage: 'XML', + } + ), }; diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/schedule_field.tsx b/x-pack/plugins/synthetics/public/components/fleet_package/schedule_field.tsx index d24095a7824c..e5d5d05a3cd7 100644 --- a/x-pack/plugins/synthetics/public/components/fleet_package/schedule_field.tsx +++ b/x-pack/plugins/synthetics/public/components/fleet_package/schedule_field.tsx @@ -32,7 +32,7 @@ export const ScheduleField = ({ number, onChange, onBlur, unit }: Props) => { { { const allOptions = [ { - text: i18n.translate('xpack.uptime.createPackagePolicy.stepConfigure.scheduleField.seconds', { - defaultMessage: 'Seconds', - }), + text: i18n.translate( + 'xpack.synthetics.createPackagePolicy.stepConfigure.scheduleField.seconds', + { + defaultMessage: 'Seconds', + } + ), value: ScheduleUnit.SECONDS, }, { - text: i18n.translate('xpack.uptime.createPackagePolicy.stepConfigure.scheduleField.minutes', { - defaultMessage: 'Minutes', - }), + text: i18n.translate( + 'xpack.synthetics.createPackagePolicy.stepConfigure.scheduleField.minutes', + { + defaultMessage: 'Minutes', + } + ), value: ScheduleUnit.MINUTES, }, ]; diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/synthetics_custom_assets_extension.tsx b/x-pack/plugins/synthetics/public/components/fleet_package/synthetics_custom_assets_extension.tsx index 65ebdf27dd37..c21acee54998 100644 --- a/x-pack/plugins/synthetics/public/components/fleet_package/synthetics_custom_assets_extension.tsx +++ b/x-pack/plugins/synthetics/public/components/fleet_package/synthetics_custom_assets_extension.tsx @@ -21,11 +21,11 @@ export const SyntheticsCustomAssetsExtension: PackageAssetsComponent = () => { const { http } = useKibana().services; const views: CustomAssetsAccordionProps['views'] = [ { - name: i18n.translate('xpack.uptime.fleetIntegration.assets.name', { + name: i18n.translate('xpack.synthetics.fleetIntegration.assets.name', { defaultMessage: 'Monitors', }), url: http?.basePath.prepend(`/app/${PLUGIN.ID}`) ?? '', - description: i18n.translate('xpack.uptime.fleetIntegration.assets.description', { + description: i18n.translate('xpack.synthetics.fleetIntegration.assets.description', { defaultMessage: 'View monitors in Uptime', }), }, diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/tcp/advanced_fields.tsx b/x-pack/plugins/synthetics/public/components/fleet_package/tcp/advanced_fields.tsx index 7598f1ec0734..adc23ecffe3f 100644 --- a/x-pack/plugins/synthetics/public/components/fleet_package/tcp/advanced_fields.tsx +++ b/x-pack/plugins/synthetics/public/components/fleet_package/tcp/advanced_fields.tsx @@ -44,14 +44,14 @@ export const TCPAdvancedFields = memo(({ children, minColumnWidth, onFiel title={

} description={ } @@ -60,14 +60,14 @@ export const TCPAdvancedFields = memo(({ children, minColumnWidth, onFiel } labelAppend={} helpText={ } @@ -91,7 +91,7 @@ export const TCPAdvancedFields = memo(({ children, minColumnWidth, onFiel checked={fields[ConfigKey.PROXY_USE_LOCAL_RESOLVER]} label={ } @@ -107,14 +107,14 @@ export const TCPAdvancedFields = memo(({ children, minColumnWidth, onFiel } labelAppend={} helpText={ } @@ -139,14 +139,14 @@ export const TCPAdvancedFields = memo(({ children, minColumnWidth, onFiel title={

} description={ } @@ -154,14 +154,14 @@ export const TCPAdvancedFields = memo(({ children, minColumnWidth, onFiel } labelAppend={} helpText={ } diff --git a/x-pack/plugins/synthetics/public/components/fleet_package/tcp/simple_fields.tsx b/x-pack/plugins/synthetics/public/components/fleet_package/tcp/simple_fields.tsx index 4eb4ed05e073..b9f5c7a990b4 100644 --- a/x-pack/plugins/synthetics/public/components/fleet_package/tcp/simple_fields.tsx +++ b/x-pack/plugins/synthetics/public/components/fleet_package/tcp/simple_fields.tsx @@ -37,14 +37,14 @@ export const TCPSimpleFields = memo(({ validate, onFieldBlur }) => { } isInvalid={!!validate[ConfigKey.HOSTS]?.(fields)} error={ } @@ -66,14 +66,14 @@ export const TCPSimpleFields = memo(({ validate, onFieldBlur }) => { id="syntheticsFleetScheduleField--number syntheticsFleetScheduleField--unit" label={ } isInvalid={!!validate[ConfigKey.SCHEDULE]?.(fields)} error={ } diff --git a/x-pack/plugins/synthetics/public/components/monitor/ml/__snapshots__/confirm_delete.test.tsx.snap b/x-pack/plugins/synthetics/public/components/monitor/ml/__snapshots__/confirm_delete.test.tsx.snap index 9d670158bc53..aeab5ef85266 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/ml/__snapshots__/confirm_delete.test.tsx.snap +++ b/x-pack/plugins/synthetics/public/components/monitor/ml/__snapshots__/confirm_delete.test.tsx.snap @@ -14,14 +14,14 @@ exports[`ML Confirm Job Delete shallow renders without errors 1`] = `

@@ -42,7 +42,7 @@ exports[`ML Confirm Job Delete shallow renders without errors while loading 1`]

) diff --git a/x-pack/plugins/synthetics/public/components/monitor/ml/confirm_alert_delete.tsx b/x-pack/plugins/synthetics/public/components/monitor/ml/confirm_alert_delete.tsx index 0d8700ff4116..0ba5f139d8f2 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/ml/confirm_alert_delete.tsx +++ b/x-pack/plugins/synthetics/public/components/monitor/ml/confirm_alert_delete.tsx @@ -29,7 +29,7 @@ export const ConfirmAlertDeletion: React.FC = ({ onConfirm, onCancel }) = >

diff --git a/x-pack/plugins/synthetics/public/components/monitor/ml/confirm_delete.tsx b/x-pack/plugins/synthetics/public/components/monitor/ml/confirm_delete.tsx index a0d1d7b5c173..ccbfbd917488 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/ml/confirm_delete.tsx +++ b/x-pack/plugins/synthetics/public/components/monitor/ml/confirm_delete.tsx @@ -31,14 +31,14 @@ export const ConfirmJobDeletion: React.FC = ({ loading, onConfirm, onCanc {!loading ? (

) : (

) @@ -47,7 +47,7 @@ export const ConfirmJobDeletion: React.FC = ({ loading, onConfirm, onCanc {!loading ? (

diff --git a/x-pack/plugins/synthetics/public/components/monitor/ml/ml_flyout.tsx b/x-pack/plugins/synthetics/public/components/monitor/ml/ml_flyout.tsx index c367b60a6501..d72b8893eeca 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/ml/ml_flyout.tsx +++ b/x-pack/plugins/synthetics/public/components/monitor/ml/ml_flyout.tsx @@ -56,7 +56,7 @@ export function MLFlyoutView({ isCreatingJob, onClickCreate, onClose, canCreateM

{labels.CREAT_ML_JOB_DESC}

diff --git a/x-pack/plugins/synthetics/public/components/monitor/ml/translations.tsx b/x-pack/plugins/synthetics/public/components/monitor/ml/translations.tsx index 6816dea66c18..9236d809177a 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/ml/translations.tsx +++ b/x-pack/plugins/synthetics/public/components/monitor/ml/translations.tsx @@ -8,14 +8,14 @@ import { i18n } from '@kbn/i18n'; export const JOB_CREATED_SUCCESS_TITLE = i18n.translate( - 'xpack.uptime.ml.enableAnomalyDetectionPanel.jobCreatedNotificationTitle', + 'xpack.synthetics.ml.enableAnomalyDetectionPanel.jobCreatedNotificationTitle', { defaultMessage: 'Job successfully created', } ); export const JOB_CREATED_SUCCESS_MESSAGE = i18n.translate( - 'xpack.uptime.ml.enableAnomalyDetectionPanel.jobCreatedNotificationText', + 'xpack.synthetics.ml.enableAnomalyDetectionPanel.jobCreatedNotificationText', { defaultMessage: 'The analysis is now running for response duration chart. It might take a while before results are added to the response times graph.', @@ -23,7 +23,7 @@ export const JOB_CREATED_SUCCESS_MESSAGE = i18n.translate( ); export const JOB_CREATED_LAZY_SUCCESS_MESSAGE = i18n.translate( - 'xpack.uptime.ml.enableAnomalyDetectionPanel.jobCreatedLazyNotificationText', + 'xpack.synthetics.ml.enableAnomalyDetectionPanel.jobCreatedLazyNotificationText', { defaultMessage: 'The analysis is waiting for an ML node to become available. It might take a while before results are added to the response times graph.', @@ -31,14 +31,14 @@ export const JOB_CREATED_LAZY_SUCCESS_MESSAGE = i18n.translate( ); export const JOB_CREATION_FAILED = i18n.translate( - 'xpack.uptime.ml.enableAnomalyDetectionPanel.jobCreationFailedNotificationTitle', + 'xpack.synthetics.ml.enableAnomalyDetectionPanel.jobCreationFailedNotificationTitle', { defaultMessage: 'Job creation failed', } ); export const JOB_CREATION_FAILED_MESSAGE = i18n.translate( - 'xpack.uptime.ml.enableAnomalyDetectionPanel.jobCreationFailedNotificationText', + 'xpack.synthetics.ml.enableAnomalyDetectionPanel.jobCreationFailedNotificationText', { defaultMessage: 'Your current license may not allow for creating machine learning jobs, or this job may already exist.', @@ -46,116 +46,119 @@ export const JOB_CREATION_FAILED_MESSAGE = i18n.translate( ); export const JOB_DELETION = i18n.translate( - 'xpack.uptime.ml.enableAnomalyDetectionPanel.jobDeletionNotificationTitle', + 'xpack.synthetics.ml.enableAnomalyDetectionPanel.jobDeletionNotificationTitle', { defaultMessage: 'Job deleted', } ); export const JOB_DELETION_SUCCESS = i18n.translate( - 'xpack.uptime.ml.enableAnomalyDetectionPanel.jobDeletionSuccessNotificationText', + 'xpack.synthetics.ml.enableAnomalyDetectionPanel.jobDeletionSuccessNotificationText', { defaultMessage: 'Job is successfully deleted', } ); export const JOB_DELETION_CONFIRMATION = i18n.translate( - 'xpack.uptime.ml.enableAnomalyDetectionPanel.jobDeletionConfirmLabel', + 'xpack.synthetics.ml.enableAnomalyDetectionPanel.jobDeletionConfirmLabel', { defaultMessage: 'Delete anomaly detection job?', } ); export const VIEW_JOB = i18n.translate( - 'xpack.uptime.ml.enableAnomalyDetectionPanel.jobCreatedNotificationText.viewJobLinkText', + 'xpack.synthetics.ml.enableAnomalyDetectionPanel.jobCreatedNotificationText.viewJobLinkText', { defaultMessage: 'View job', } ); -export const EXPLORE_IN_ML_APP = i18n.translate('xpack.uptime.ml.durationChart.exploreInMlApp', { - defaultMessage: 'Explore in ML App', -}); +export const EXPLORE_IN_ML_APP = i18n.translate( + 'xpack.synthetics.ml.durationChart.exploreInMlApp', + { + defaultMessage: 'Explore in ML App', + } +); export const ENABLE_ANOMALY_DETECTION = i18n.translate( - 'xpack.uptime.ml.enableAnomalyDetectionPanel.enableAnomalyDetectionTitle', + 'xpack.synthetics.ml.enableAnomalyDetectionPanel.enableAnomalyDetectionTitle', { defaultMessage: 'Enable anomaly detection', } ); export const ANOMALY_DETECTION = i18n.translate( - 'xpack.uptime.ml.enableAnomalyDetectionPanel.anomalyDetectionTitle', + 'xpack.synthetics.ml.enableAnomalyDetectionPanel.anomalyDetectionTitle', { defaultMessage: 'Anomaly detection', } ); export const DISABLE_ANOMALY_DETECTION = i18n.translate( - 'xpack.uptime.ml.enableAnomalyDetectionPanel.disableAnomalyDetectionTitle', + 'xpack.synthetics.ml.enableAnomalyDetectionPanel.disableAnomalyDetectionTitle', { defaultMessage: 'Disable anomaly detection', } ); export const ENABLE_ANOMALY_ALERT = i18n.translate( - 'xpack.uptime.ml.enableAnomalyDetectionPanel.enableAnomalyAlert', + 'xpack.synthetics.ml.enableAnomalyDetectionPanel.enableAnomalyAlert', { defaultMessage: 'Enable anomaly alert', } ); export const ENABLE_ANOMALY_NO_PERMISSIONS_TOOLTIP = i18n.translate( - 'xpack.uptime.ml.enableAnomalyDetectionPanel.noPermissionsTooltip', + 'xpack.synthetics.ml.enableAnomalyDetectionPanel.noPermissionsTooltip', { defaultMessage: 'You need read-write access to Uptime to create anomaly alerts.', } ); export const DISABLE_ANOMALY_ALERT = i18n.translate( - 'xpack.uptime.ml.enableAnomalyDetectionPanel.disableAnomalyAlert', + 'xpack.synthetics.ml.enableAnomalyDetectionPanel.disableAnomalyAlert', { defaultMessage: 'Disable anomaly alert', } ); export const MANAGE_ANOMALY_DETECTION = i18n.translate( - 'xpack.uptime.ml.enableAnomalyDetectionPanel.manageAnomalyDetectionTitle', + 'xpack.synthetics.ml.enableAnomalyDetectionPanel.manageAnomalyDetectionTitle', { defaultMessage: 'Manage anomaly detection', } ); export const ML_MANAGEMENT_PAGE = i18n.translate( - 'xpack.uptime.ml.enableAnomalyDetectionPanel.manageMLJobDescription.mlJobsPageLinkText', + 'xpack.synthetics.ml.enableAnomalyDetectionPanel.manageMLJobDescription.mlJobsPageLinkText', { defaultMessage: 'Machine Learning jobs management page', } ); export const TAKE_SOME_TIME_TEXT = i18n.translate( - 'xpack.uptime.ml.enableAnomalyDetectionPanel.manageMLJobDescription.noteText', + 'xpack.synthetics.ml.enableAnomalyDetectionPanel.manageMLJobDescription.noteText', { defaultMessage: 'Note: It might take a few minutes for the job to begin calculating results.', } ); export const CREATE_NEW_JOB = i18n.translate( - 'xpack.uptime.ml.enableAnomalyDetectionPanel.createNewJobButtonLabel', + 'xpack.synthetics.ml.enableAnomalyDetectionPanel.createNewJobButtonLabel', { defaultMessage: 'Create new job', } ); export const CANCEL_LABEL = i18n.translate( - 'xpack.uptime.ml.enableAnomalyDetectionPanel.cancelLabel', + 'xpack.synthetics.ml.enableAnomalyDetectionPanel.cancelLabel', { defaultMessage: 'Cancel', } ); export const CREAT_ML_JOB_DESC = i18n.translate( - 'xpack.uptime.ml.enableAnomalyDetectionPanel.createMLJobDescription', + 'xpack.synthetics.ml.enableAnomalyDetectionPanel.createMLJobDescription', { defaultMessage: `Here you can create a machine learning job to calculate anomaly scores on response durations for Uptime Monitor. Once enabled, the monitor duration chart on the details page @@ -165,14 +168,14 @@ export const CREAT_ML_JOB_DESC = i18n.translate( ); export const START_TRAIL = i18n.translate( - 'xpack.uptime.ml.enableAnomalyDetectionPanel.startTrial', + 'xpack.synthetics.ml.enableAnomalyDetectionPanel.startTrial', { defaultMessage: 'Start free 14-day trial', } ); export const START_TRAIL_DESC = i18n.translate( - 'xpack.uptime.ml.enableAnomalyDetectionPanel.startTrialDesc', + 'xpack.synthetics.ml.enableAnomalyDetectionPanel.startTrialDesc', { defaultMessage: 'In order to access duration anomaly detection, you have to be subscribed to an Elastic Platinum license.', @@ -180,7 +183,7 @@ export const START_TRAIL_DESC = i18n.translate( ); export const ENABLE_MANAGE_JOB = i18n.translate( - 'xpack.uptime.ml.enableAnomalyDetectionPanel.enable_or_manage_job', + 'xpack.synthetics.ml.enableAnomalyDetectionPanel.enable_or_manage_job', { defaultMessage: 'You can enable anomaly detection job or if job is already there you can manage the job or alert.', @@ -188,7 +191,7 @@ export const ENABLE_MANAGE_JOB = i18n.translate( ); export const ADD_JOB_PERMISSIONS_NEEDED = i18n.translate( - 'xpack.uptime.ml.enableAnomalyDetectionPanel.add_job_permissions_needed', + 'xpack.synthetics.ml.enableAnomalyDetectionPanel.add_job_permissions_needed', { defaultMessage: 'Permissions needed', } diff --git a/x-pack/plugins/synthetics/public/components/monitor/monitor_duration/monitor_duration.tsx b/x-pack/plugins/synthetics/public/components/monitor/monitor_duration/monitor_duration.tsx index dbd8846707d1..6d8615226f79 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/monitor_duration/monitor_duration.tsx +++ b/x-pack/plugins/synthetics/public/components/monitor/monitor_duration/monitor_duration.tsx @@ -41,13 +41,13 @@ export const MonitorDurationComponent = ({

{hasMLJob ? ( ) : ( )} @@ -59,7 +59,7 @@ export const MonitorDurationComponent = ({ {/* */} {/* */} - {/* */} + {/* */} {/* */} {/* */} diff --git a/x-pack/plugins/synthetics/public/components/monitor/monitor_title.tsx b/x-pack/plugins/synthetics/public/components/monitor/monitor_title.tsx index 8cbc9eed0a21..0d77179cd99e 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/monitor_title.tsx +++ b/x-pack/plugins/synthetics/public/components/monitor/monitor_title.tsx @@ -42,28 +42,28 @@ export const MonitorPageTitleContent: React.FC = () => { case 'http': return ( ); case 'tcp': return ( ); case 'icmp': return ( ); case 'browser': return ( ); @@ -85,7 +85,7 @@ export const MonitorPageTitleContent: React.FC = () => { {renderMonitorType(type)}{' '} @@ -96,7 +96,7 @@ export const MonitorPageTitleContent: React.FC = () => { diff --git a/x-pack/plugins/synthetics/public/components/monitor/ping_list/columns/expand_row.tsx b/x-pack/plugins/synthetics/public/components/monitor/ping_list/columns/expand_row.tsx index b0453dd8f268..1c1866874129 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/ping_list/columns/expand_row.tsx +++ b/x-pack/plugins/synthetics/public/components/monitor/ping_list/columns/expand_row.tsx @@ -50,10 +50,10 @@ export const ExpandRowColumn = ({ item, expandedRows, setExpandedRows }: Props) isDisabled={!rowShouldExpand(item)} aria-label={ expandedRows[item.docId] - ? i18n.translate('xpack.uptime.pingList.collapseRow', { + ? i18n.translate('xpack.synthetics.pingList.collapseRow', { defaultMessage: 'Collapse', }) - : i18n.translate('xpack.uptime.pingList.expandRow', { defaultMessage: 'Expand' }) + : i18n.translate('xpack.synthetics.pingList.expandRow', { defaultMessage: 'Expand' }) } iconType={expandedRows[item.docId] ? 'arrowUp' : 'arrowDown'} /> diff --git a/x-pack/plugins/synthetics/public/components/monitor/ping_list/columns/ping_status.tsx b/x-pack/plugins/synthetics/public/components/monitor/ping_list/columns/ping_status.tsx index dcf3fdb9163d..6c3301812698 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/ping_list/columns/ping_status.tsx +++ b/x-pack/plugins/synthetics/public/components/monitor/ping_list/columns/ping_status.tsx @@ -47,7 +47,7 @@ export const PingStatusColumn = ({ pingStatus, item }: Props) => { } return ( -
+
{ - {i18n.translate('xpack.uptime.pingList.recencyMessage', { + {i18n.translate('xpack.synthetics.pingList.recencyMessage', { values: { fromNow: checkedTime }, defaultMessage: 'Checked {fromNow}', description: diff --git a/x-pack/plugins/synthetics/public/components/monitor/ping_list/columns/ping_timestamp/no_image_available.tsx b/x-pack/plugins/synthetics/public/components/monitor/ping_list/columns/ping_timestamp/no_image_available.tsx index 4790b8c1dd37..62c1392a2ed5 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/ping_list/columns/ping_timestamp/no_image_available.tsx +++ b/x-pack/plugins/synthetics/public/components/monitor/ping_list/columns/ping_timestamp/no_image_available.tsx @@ -21,7 +21,7 @@ export const NoImageAvailable = () => { diff --git a/x-pack/plugins/synthetics/public/components/monitor/ping_list/columns/ping_timestamp/translations.ts b/x-pack/plugins/synthetics/public/components/monitor/ping_list/columns/ping_timestamp/translations.ts index f781610745ce..065af8f7ff5d 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/ping_list/columns/ping_timestamp/translations.ts +++ b/x-pack/plugins/synthetics/public/components/monitor/ping_list/columns/ping_timestamp/translations.ts @@ -7,27 +7,36 @@ import { i18n } from '@kbn/i18n'; -export const prevAriaLabel = i18n.translate('xpack.uptime.synthetics.prevStepButton.airaLabel', { - defaultMessage: 'Previous step', -}); +export const prevAriaLabel = i18n.translate( + 'xpack.synthetics.synthetics.prevStepButton.airaLabel', + { + defaultMessage: 'Previous step', + } +); -export const nextAriaLabel = i18n.translate('xpack.uptime.synthetics.nextStepButton.ariaLabel', { - defaultMessage: 'Next step', -}); +export const nextAriaLabel = i18n.translate( + 'xpack.synthetics.synthetics.nextStepButton.ariaLabel', + { + defaultMessage: 'Next step', + } +); export const imageLoadingSpinnerAriaLabel = i18n.translate( - 'xpack.uptime.synthetics.imageLoadingSpinner.ariaLabel', + 'xpack.synthetics.synthetics.imageLoadingSpinner.ariaLabel', { defaultMessage: 'An animated spinner indicating the image is loading', } ); -export const fullSizeImageAlt = i18n.translate('xpack.uptime.synthetics.thumbnail.fullSize.alt', { - defaultMessage: `A larger version of the screenshot for this journey step's thumbnail.`, -}); +export const fullSizeImageAlt = i18n.translate( + 'xpack.synthetics.synthetics.thumbnail.fullSize.alt', + { + defaultMessage: `A larger version of the screenshot for this journey step's thumbnail.`, + } +); export const formatCaptionContent = (stepNumber: number, totalSteps?: number) => - i18n.translate('xpack.uptime.synthetics.pingTimestamp.captionContent', { + i18n.translate('xpack.synthetics.synthetics.pingTimestamp.captionContent', { defaultMessage: 'Step: {stepNumber} of {totalSteps}', values: { stepNumber, diff --git a/x-pack/plugins/synthetics/public/components/monitor/ping_list/doc_link_body.tsx b/x-pack/plugins/synthetics/public/components/monitor/ping_list/doc_link_body.tsx index f56df517455d..d917cd1aeb5a 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/ping_list/doc_link_body.tsx +++ b/x-pack/plugins/synthetics/public/components/monitor/ping_list/doc_link_body.tsx @@ -16,7 +16,7 @@ const bodyDocsLink = export const DocLinkForBody = () => { const docsLink = ( - {i18n.translate('xpack.uptime.pingList.drawer.body.docsLink', { + {i18n.translate('xpack.synthetics.pingList.drawer.body.docsLink', { defaultMessage: 'docs', description: 'Docs link to set response body', })} @@ -26,7 +26,7 @@ export const DocLinkForBody = () => { return ( diff --git a/x-pack/plugins/synthetics/public/components/monitor/ping_list/expanded_row.tsx b/x-pack/plugins/synthetics/public/components/monitor/ping_list/expanded_row.tsx index df0d273d3bc3..3e14136ac353 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/ping_list/expanded_row.tsx +++ b/x-pack/plugins/synthetics/public/components/monitor/ping_list/expanded_row.tsx @@ -33,14 +33,14 @@ const BodyDescription = ({ body }: { body: HttpResponseBody }) => { const truncatedText = contentBytes > 0 && contentBytes < bodyBytes - ? i18n.translate('xpack.uptime.pingList.expandedRow.truncated', { + ? i18n.translate('xpack.synthetics.pingList.expandedRow.truncated', { defaultMessage: 'Showing first {contentBytes} bytes.', values: { contentBytes }, }) : null; const bodySizeText = bodyBytes > 0 - ? i18n.translate('xpack.uptime.pingList.expandedRow.bodySize', { + ? i18n.translate('xpack.synthetics.pingList.expandedRow.bodySize', { defaultMessage: 'Body size is {bodyBytes}.', values: { bodyBytes: formatNumber(bodyBytes, '0b') }, }) @@ -59,7 +59,9 @@ export const PingListExpandedRowComponent = ({ ping }: Props) => { // Show the error block if (ping.error) { listItems.push({ - title: i18n.translate('xpack.uptime.pingList.expandedRow.error', { defaultMessage: 'Error' }), + title: i18n.translate('xpack.synthetics.pingList.expandedRow.error', { + defaultMessage: 'Error', + }), description: {ping.error.message}, }); } @@ -69,7 +71,7 @@ export const PingListExpandedRowComponent = ({ ping }: Props) => { const body = ping.http.response.body; listItems.push({ - title: i18n.translate('xpack.uptime.pingList.expandedRow.response_body', { + title: i18n.translate('xpack.synthetics.pingList.expandedRow.response_body', { defaultMessage: 'Response Body', }), description: ( diff --git a/x-pack/plugins/synthetics/public/components/monitor/ping_list/headers.tsx b/x-pack/plugins/synthetics/public/components/monitor/ping_list/headers.tsx index 7740116d9f36..edc8119dd59d 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/ping_list/headers.tsx +++ b/x-pack/plugins/synthetics/public/components/monitor/ping_list/headers.tsx @@ -30,7 +30,7 @@ export const PingHeaders = ({ headers }: Props) => { buttonContent={

- {i18n.translate('xpack.uptime.pingList.headers.title', { + {i18n.translate('xpack.synthetics.pingList.headers.title', { defaultMessage: 'Response headers', })}

diff --git a/x-pack/plugins/synthetics/public/components/monitor/ping_list/location_name.tsx b/x-pack/plugins/synthetics/public/components/monitor/ping_list/location_name.tsx index dd1f65789fdd..81c59a85d416 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/ping_list/location_name.tsx +++ b/x-pack/plugins/synthetics/public/components/monitor/ping_list/location_name.tsx @@ -20,7 +20,7 @@ export const LocationName = ({ location }: LocationNameProps) => href="https://www.elastic.co/guide/en/beats/heartbeat/current/configuration-observer-options.html" target="_blank" > - {i18n.translate('xpack.uptime.locationName.helpLinkAnnotation', { + {i18n.translate('xpack.synthetics.locationName.helpLinkAnnotation', { defaultMessage: 'Add location', description: 'Text that instructs the user to navigate to our docs to add a geographic location to their data', diff --git a/x-pack/plugins/synthetics/public/components/monitor/ping_list/ping_list.tsx b/x-pack/plugins/synthetics/public/components/monitor/ping_list/ping_list.tsx index 27141aa436d6..948172e1ea68 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/ping_list/ping_list.tsx +++ b/x-pack/plugins/synthetics/public/components/monitor/ping_list/ping_list.tsx @@ -29,7 +29,7 @@ const MILLIS_LIMIT = ONE_SECOND_AS_MICROS * 1; export const formatDuration = (durationMicros: number) => { if (durationMicros < MILLIS_LIMIT) { - return i18n.translate('xpack.uptime.pingList.durationMsColumnFormatting', { + return i18n.translate('xpack.synthetics.pingList.durationMsColumnFormatting', { values: { millis: microsToMillis(durationMicros) }, defaultMessage: '{millis} ms', }); @@ -39,12 +39,12 @@ export const formatDuration = (durationMicros: number) => { // we format seconds with correct pluralization here and not for `ms` because it is much more likely users // will encounter times of exactly '1' second. if (seconds === '1') { - return i18n.translate('xpack.uptime.pingist.durationSecondsColumnFormatting.singular', { + return i18n.translate('xpack.synthetics.pingist.durationSecondsColumnFormatting.singular', { values: { seconds }, defaultMessage: '{seconds} second', }); } - return i18n.translate('xpack.uptime.pingist.durationSecondsColumnFormatting', { + return i18n.translate('xpack.synthetics.pingist.durationSecondsColumnFormatting', { values: { seconds }, defaultMessage: '{seconds} seconds', }); diff --git a/x-pack/plugins/synthetics/public/components/monitor/ping_list/ping_list_header.tsx b/x-pack/plugins/synthetics/public/components/monitor/ping_list/ping_list_header.tsx index b94663630aee..51e86627a1b0 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/ping_list/ping_list_header.tsx +++ b/x-pack/plugins/synthetics/public/components/monitor/ping_list/ping_list_header.tsx @@ -18,7 +18,7 @@ export const PingListHeader = () => {

diff --git a/x-pack/plugins/synthetics/public/components/monitor/ping_list/ping_list_table.tsx b/x-pack/plugins/synthetics/public/components/monitor/ping_list/ping_list_table.tsx index 5e2737684b33..da464b7e1a08 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/ping_list/ping_list_table.tsx +++ b/x-pack/plugins/synthetics/public/components/monitor/ping_list/ping_list_table.tsx @@ -128,7 +128,7 @@ export function PingListTable({ loading, error, pings, pagination, onChange, fai align: 'right', dataType: 'number', field: 'monitor.ip', - name: i18n.translate('xpack.uptime.pingList.ipAddressColumnLabel', { + name: i18n.translate('xpack.synthetics.pingList.ipAddressColumnLabel', { defaultMessage: 'IP', }), }, @@ -137,7 +137,7 @@ export function PingListTable({ loading, error, pings, pagination, onChange, fai { align: 'center', field: 'monitor.duration.us', - name: i18n.translate('xpack.uptime.pingList.durationMsColumnLabel', { + name: i18n.translate('xpack.synthetics.pingList.durationMsColumnLabel', { defaultMessage: 'Duration', }), render: (duration: number | null) => @@ -164,7 +164,7 @@ export function PingListTable({ loading, error, pings, pagination, onChange, fai { field: 'monitor.status', align: 'left', - name: i18n.translate('xpack.uptime.pingList.columns.failedStep', { + name: i18n.translate('xpack.synthetics.pingList.columns.failedStep', { defaultMessage: 'Failed step', }), render: (_timestamp: string, item: Ping) => ( @@ -234,10 +234,10 @@ export function PingListTable({ loading, error, pings, pagination, onChange, fai pagination={pagination} noItemsMessage={ loading - ? i18n.translate('xpack.uptime.pingList.pingsLoadingMesssage', { + ? i18n.translate('xpack.synthetics.pingList.pingsLoadingMesssage', { defaultMessage: 'Loading history...', }) - : i18n.translate('xpack.uptime.pingList.pingsUnavailableMessage', { + : i18n.translate('xpack.synthetics.pingList.pingsUnavailableMessage', { defaultMessage: 'No history found', }) } diff --git a/x-pack/plugins/synthetics/public/components/monitor/ping_list/ping_redirects.tsx b/x-pack/plugins/synthetics/public/components/monitor/ping_list/ping_redirects.tsx index 01e0cb5be2b0..37e5961305a6 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/ping_list/ping_redirects.tsx +++ b/x-pack/plugins/synthetics/public/components/monitor/ping_list/ping_redirects.tsx @@ -41,7 +41,7 @@ export const PingRedirects: React.FC = ({ monitorStatus, showTitle }) => iconType: 'popout', iconSize: 's', alwaysShow: true, - 'aria-label': i18n.translate('xpack.uptime.monitorList.redirects.openWindow', { + 'aria-label': i18n.translate('xpack.synthetics.monitorList.redirects.openWindow', { defaultMessage: 'Link will open in new window.', }), }, @@ -59,7 +59,7 @@ export const PingRedirects: React.FC = ({ monitorStatus, showTitle }) => color: 'text', iconType: 'popout', iconSize: 's', - 'aria-label': i18n.translate('xpack.uptime.monitorList.redirects.openWindow', { + 'aria-label': i18n.translate('xpack.synthetics.monitorList.redirects.openWindow', { defaultMessage: 'Link will open in new window.', }), alwaysShow: true, @@ -74,7 +74,7 @@ export const PingRedirects: React.FC = ({ monitorStatus, showTitle }) => {showTitle && (

- {i18n.translate('xpack.uptime.monitorList.redirects.title', { + {i18n.translate('xpack.synthetics.monitorList.redirects.title', { defaultMessage: 'Redirects', })}

@@ -83,7 +83,7 @@ export const PingRedirects: React.FC = ({ monitorStatus, showTitle }) => { - {i18n.translate('xpack.uptime.monitorList.redirects.description', { + {i18n.translate('xpack.synthetics.monitorList.redirects.description', { defaultMessage: 'Heartbeat followed {number} redirects while executing ping.', values: { number: list?.length ?? 0, diff --git a/x-pack/plugins/synthetics/public/components/monitor/ping_list/translations.ts b/x-pack/plugins/synthetics/public/components/monitor/ping_list/translations.ts index 674b5c32332b..81e4451f9103 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/ping_list/translations.ts +++ b/x-pack/plugins/synthetics/public/components/monitor/ping_list/translations.ts @@ -7,24 +7,24 @@ import { i18n } from '@kbn/i18n'; -export const STATUS_LABEL = i18n.translate('xpack.uptime.pingList.statusColumnLabel', { +export const STATUS_LABEL = i18n.translate('xpack.synthetics.pingList.statusColumnLabel', { defaultMessage: 'Status', }); -export const RES_CODE_LABEL = i18n.translate('xpack.uptime.pingList.responseCodeColumnLabel', { +export const RES_CODE_LABEL = i18n.translate('xpack.synthetics.pingList.responseCodeColumnLabel', { defaultMessage: 'Response code', }); -export const ERROR_TYPE_LABEL = i18n.translate('xpack.uptime.pingList.errorTypeColumnLabel', { +export const ERROR_TYPE_LABEL = i18n.translate('xpack.synthetics.pingList.errorTypeColumnLabel', { defaultMessage: 'Error type', }); -export const ERROR_LABEL = i18n.translate('xpack.uptime.pingList.errorColumnLabel', { +export const ERROR_LABEL = i18n.translate('xpack.synthetics.pingList.errorColumnLabel', { defaultMessage: 'Error', }); -export const LOCATION_LABEL = i18n.translate('xpack.uptime.pingList.locationNameColumnLabel', { +export const LOCATION_LABEL = i18n.translate('xpack.synthetics.pingList.locationNameColumnLabel', { defaultMessage: 'Location', }); -export const TIMESTAMP_LABEL = i18n.translate('xpack.uptime.pingList.timestampColumnLabel', { +export const TIMESTAMP_LABEL = i18n.translate('xpack.synthetics.pingList.timestampColumnLabel', { defaultMessage: 'Timestamp', }); diff --git a/x-pack/plugins/synthetics/public/components/monitor/status_details/__snapshots__/status_by_location.test.tsx.snap b/x-pack/plugins/synthetics/public/components/monitor/status_details/__snapshots__/status_by_location.test.tsx.snap index 449038f1023d..7bb4c36d3d0d 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/status_details/__snapshots__/status_by_location.test.tsx.snap +++ b/x-pack/plugins/synthetics/public/components/monitor/status_details/__snapshots__/status_by_location.test.tsx.snap @@ -15,7 +15,7 @@ exports[`StatusByLocation component renders properly against props 1`] = `

= ({ allLocations }) => { return ( = ({ monitorStatus }) => { onClick={() => setIsPopoverOpen(!isPopoverOpen)} data-test-subj="uptimeMonitorRedirectInfo" > - {i18n.translate('xpack.uptime.monitorList.redirects.title.number', { + {i18n.translate('xpack.synthetics.monitorList.redirects.title.number', { defaultMessage: '{number}', values: { number: list?.length ?? 0, diff --git a/x-pack/plugins/synthetics/public/components/monitor/status_details/status_bar/ssl_certificate.tsx b/x-pack/plugins/synthetics/public/components/monitor/status_details/status_bar/ssl_certificate.tsx index ca47db608451..13be917075ac 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/status_details/status_bar/ssl_certificate.tsx +++ b/x-pack/plugins/synthetics/public/components/monitor/status_details/status_bar/ssl_certificate.tsx @@ -40,7 +40,7 @@ export const MonitorSSLCertificate = ({ tls }: Props) => { <> diff --git a/x-pack/plugins/synthetics/public/components/monitor/status_details/status_bar/status_bar.tsx b/x-pack/plugins/synthetics/public/components/monitor/status_details/status_bar/status_bar.tsx index c9da27d01848..9746a6c676ab 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/status_details/status_bar/status_bar.tsx +++ b/x-pack/plugins/synthetics/public/components/monitor/status_details/status_bar/status_bar.tsx @@ -44,19 +44,19 @@ export const MonListDescription = styled(EuiDescriptionListDescription)` export const renderMonitorType = (type: string | undefined) => { switch (type) { case 'http': - return i18n.translate('xpack.uptime.monitorDetails.statusBar.pingType.http', { + return i18n.translate('xpack.synthetics.monitorDetails.statusBar.pingType.http', { defaultMessage: 'HTTP', }); case 'tcp': - return i18n.translate('xpack.uptime.monitorDetails.statusBar.pingType.tcp', { + return i18n.translate('xpack.synthetics.monitorDetails.statusBar.pingType.tcp', { defaultMessage: 'TCP', }); case 'icmp': - return i18n.translate('xpack.uptime.monitorDetails.statusBar.pingType.icmp', { + return i18n.translate('xpack.synthetics.monitorDetails.statusBar.pingType.icmp', { defaultMessage: 'ICMP', }); case 'browser': - return i18n.translate('xpack.uptime.monitorDetails.statusBar.pingType.browser', { + return i18n.translate('xpack.synthetics.monitorDetails.statusBar.pingType.browser', { defaultMessage: 'Browser', }); default: @@ -83,7 +83,7 @@ export const MonitorStatusBar: React.FC = () => { {OverallAvailability} {

{locations.length <= 1 ? ( { /> ) : ( = ({ checkGroup, stepIndex @@ -88,7 +94,7 @@ export const WaterfallChartContainer: React.FC = ({ checkGroup, stepIndex } @@ -96,7 +102,7 @@ export const WaterfallChartContainer: React.FC = ({ checkGroup, stepIndex iconType="help" > diff --git a/x-pack/plugins/synthetics/public/components/monitor/synthetics/step_detail/waterfall/waterfall_flyout.tsx b/x-pack/plugins/synthetics/public/components/monitor/synthetics/step_detail/waterfall/waterfall_flyout.tsx index bdcea95c0c0c..4c78010e9c4e 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/synthetics/step_detail/waterfall/waterfall_flyout.tsx +++ b/x-pack/plugins/synthetics/public/components/monitor/synthetics/step_detail/waterfall/waterfall_flyout.tsx @@ -24,26 +24,26 @@ import { MiddleTruncatedText } from '../../waterfall'; import { WaterfallMetadataEntry } from '../../waterfall/types'; import { OnFlyoutClose } from '../../waterfall/components/use_flyout'; -export const DETAILS = i18n.translate('xpack.uptime.synthetics.waterfall.flyout.details', { +export const DETAILS = i18n.translate('xpack.synthetics.synthetics.waterfall.flyout.details', { defaultMessage: 'Details', }); export const CERTIFICATES = i18n.translate( - 'xpack.uptime.synthetics.waterfall.flyout.certificates', + 'xpack.synthetics.synthetics.waterfall.flyout.certificates', { defaultMessage: 'Certificate headers', } ); export const REQUEST_HEADERS = i18n.translate( - 'xpack.uptime.synthetics.waterfall.flyout.requestHeaders', + 'xpack.synthetics.synthetics.waterfall.flyout.requestHeaders', { defaultMessage: 'Request headers', } ); export const RESPONSE_HEADERS = i18n.translate( - 'xpack.uptime.synthetics.waterfall.flyout.responseHeaders', + 'xpack.synthetics.synthetics.waterfall.flyout.responseHeaders', { defaultMessage: 'Response headers', } diff --git a/x-pack/plugins/synthetics/public/components/monitor/synthetics/translations.ts b/x-pack/plugins/synthetics/public/components/monitor/synthetics/translations.ts index bc9bfb5fb352..cf7ab30c8867 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/synthetics/translations.ts +++ b/x-pack/plugins/synthetics/public/components/monitor/synthetics/translations.ts @@ -8,7 +8,7 @@ import { i18n } from '@kbn/i18n'; export const VIEW_PERFORMANCE = i18n.translate( - 'xpack.uptime.pingList.synthetics.performanceBreakDown', + 'xpack.synthetics.pingList.synthetics.performanceBreakDown', { defaultMessage: 'View performance breakdown', } diff --git a/x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/middle_truncated_text.tsx b/x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/middle_truncated_text.tsx index f649a5f2d373..7da52b2ce9bc 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/middle_truncated_text.tsx +++ b/x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/middle_truncated_text.tsx @@ -146,9 +146,12 @@ export const MiddleTruncatedText = ({ type="lock" size="s" color="success" - aria-label={i18n.translate('xpack.uptime.waterfallChart.sidebar.url.https', { - defaultMessage: 'https', - })} + aria-label={i18n.translate( + 'xpack.synthetics.waterfallChart.sidebar.url.https', + { + defaultMessage: 'https', + } + )} /> )} {chunks.first} @@ -170,7 +173,7 @@ export const MiddleTruncatedText = ({ diff --git a/x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/network_requests_total.tsx b/x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/network_requests_total.tsx index fe45c45b13d2..4fac0b3cd00d 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/network_requests_total.tsx +++ b/x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/network_requests_total.tsx @@ -28,13 +28,13 @@ export const NetworkRequestsTotal = ({ fetchedNetworkRequests ? ( @@ -45,7 +45,7 @@ export const NetworkRequestsTotal = ({ />{' '} {showHighlightedNetworkRequests && highlightedNetworkRequests >= 0 && ( fetchedNetworkRequests && ( )} diff --git a/x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/translations.ts b/x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/translations.ts index b63ffacaadd2..6bb0c03f7b99 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/translations.ts +++ b/x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/translations.ts @@ -8,42 +8,42 @@ import { i18n } from '@kbn/i18n'; export const FILTER_REQUESTS_LABEL = i18n.translate( - 'xpack.uptime.synthetics.waterfall.searchBox.placeholder', + 'xpack.synthetics.synthetics.waterfall.searchBox.placeholder', { defaultMessage: 'Filter network requests', } ); export const FILTER_SCREENREADER_LABEL = i18n.translate( - 'xpack.uptime.synthetics.waterfall.filterGroup.filterScreenreaderLabel', + 'xpack.synthetics.synthetics.waterfall.filterGroup.filterScreenreaderLabel', { defaultMessage: 'Filter by', } ); export const FILTER_REMOVE_SCREENREADER_LABEL = i18n.translate( - 'xpack.uptime.synthetics.waterfall.filterGroup.removeFilterScreenReaderLabel', + 'xpack.synthetics.synthetics.waterfall.filterGroup.removeFilterScreenReaderLabel', { defaultMessage: 'Remove filter by', } ); export const FILTER_POPOVER_OPEN_LABEL = i18n.translate( - 'xpack.uptime.pingList.synthetics.waterfall.filters.popover', + 'xpack.synthetics.pingList.synthetics.waterfall.filters.popover', { defaultMessage: 'Click to open waterfall filters', } ); export const FILTER_COLLAPSE_REQUESTS_LABEL = i18n.translate( - 'xpack.uptime.pingList.synthetics.waterfall.filters.collapseRequestsLabel', + 'xpack.synthetics.pingList.synthetics.waterfall.filters.collapseRequestsLabel', { defaultMessage: 'Collapse to only show matching requests', } ); export const SIDEBAR_FILTER_MATCHES_SCREENREADER_LABEL = i18n.translate( - 'xpack.uptime.synthetics.waterfall.sidebar.filterMatchesScreenReaderLabel', + 'xpack.synthetics.synthetics.waterfall.sidebar.filterMatchesScreenReaderLabel', { defaultMessage: 'Resource matches filter', } diff --git a/x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/waterfall_marker_icon.tsx b/x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/waterfall_marker_icon.tsx index 4bef5fb04152..675224bfa8f7 100644 --- a/x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/waterfall_marker_icon.tsx +++ b/x-pack/plugins/synthetics/public/components/monitor/synthetics/waterfall/components/waterfall_marker_icon.tsx @@ -16,7 +16,7 @@ export function WaterfallMarkerIcon({ field, label }: { field: string; label: st if (!field) { return ( { ); }; -const ADD_MONITOR_LABEL = i18n.translate('xpack.uptime.monitorManagement.addMonitorLabel', { +const ADD_MONITOR_LABEL = i18n.translate('xpack.synthetics.monitorManagement.addMonitorLabel', { defaultMessage: 'Add monitor', }); const SYNTHETICS_ENABLE_LABEL = i18n.translate( - 'xpack.uptime.monitorManagement.syntheticsEnableLabel', + 'xpack.synthetics.monitorManagement.syntheticsEnableLabel', { defaultMessage: 'Enable', } ); const SYNTHETICS_ENABLE_FAILURE = i18n.translate( - 'xpack.uptime.monitorManagement.syntheticsEnabledFailure', + 'xpack.synthetics.monitorManagement.syntheticsEnabledFailure', { defaultMessage: 'Monitor Management was not able to be enabled. Please contact support.', } ); const SYNTHETICS_DISABLE_FAILURE = i18n.translate( - 'xpack.uptime.monitorManagement.syntheticsDisabledFailure', + 'xpack.synthetics.monitorManagement.syntheticsDisabledFailure', { defaultMessage: 'Monitor Management was not able to be disabled. Please contact support.', } ); const SYNTHETICS_ENABLE_SUCCESS = i18n.translate( - 'xpack.uptime.monitorManagement.syntheticsEnableSuccess', + 'xpack.synthetics.monitorManagement.syntheticsEnableSuccess', { defaultMessage: 'Monitor Management enabled successfully.', } ); const SYNTHETICS_DISABLE_SUCCESS = i18n.translate( - 'xpack.uptime.monitorManagement.syntheticsDisabledSuccess', + 'xpack.synthetics.monitorManagement.syntheticsDisabledSuccess', { defaultMessage: 'Monitor Management disabled successfully.', } ); const SYNTHETICS_DISABLED_MESSAGE = i18n.translate( - 'xpack.uptime.monitorManagement.syntheticsDisabled', + 'xpack.synthetics.monitorManagement.syntheticsDisabled', { defaultMessage: 'Monitor Management is currently disabled. Please contact an administrator to enable Monitor Management.', @@ -183,7 +183,7 @@ const SYNTHETICS_DISABLED_MESSAGE = i18n.translate( ); const SYNTHETICS_ENABLE_TOOL_TIP_MESSAGE = i18n.translate( - 'xpack.uptime.monitorManagement.syntheticsEnableToolTip', + 'xpack.synthetics.monitorManagement.syntheticsEnableToolTip', { defaultMessage: 'Enable Monitor Management to create lightweight and real-browser monitors from locations around the world.', @@ -191,7 +191,7 @@ const SYNTHETICS_ENABLE_TOOL_TIP_MESSAGE = i18n.translate( ); const SYNTHETICS_DISABLE_TOOL_TIP_MESSAGE = i18n.translate( - 'xpack.uptime.monitorManagement.syntheticsDisableToolTip', + 'xpack.synthetics.monitorManagement.syntheticsDisableToolTip', { defaultMessage: 'Disabling Monitor Management will immediately stop the execution of monitors in all test locations and prevent the creation of new monitors.', @@ -199,7 +199,7 @@ const SYNTHETICS_DISABLE_TOOL_TIP_MESSAGE = i18n.translate( ); const API_KEYS_DISABLED_TOOL_TIP_MESSAGE = i18n.translate( - 'xpack.uptime.monitorManagement.apiKeysDisabledToolTip', + 'xpack.synthetics.monitorManagement.apiKeysDisabledToolTip', { defaultMessage: 'API Keys are disabled for this cluster. Monitor Management requires the use of API keys to write back to your Elasticsearch cluster. To enable API keys, please contact an administrator.', diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/content/index.ts b/x-pack/plugins/synthetics/public/components/monitor_management/content/index.ts index 369506a12b3d..8c88fb8944a2 100644 --- a/x-pack/plugins/synthetics/public/components/monitor_management/content/index.ts +++ b/x-pack/plugins/synthetics/public/components/monitor_management/content/index.ts @@ -8,28 +8,28 @@ import { i18n } from '@kbn/i18n'; export const SYNTHETICS_ENABLE_FAILURE = i18n.translate( - 'xpack.uptime.monitorManagement.syntheticsEnabledFailure', + 'xpack.synthetics.monitorManagement.syntheticsEnabledFailure', { defaultMessage: 'Monitor Management was not able to be enabled. Please contact support.', } ); export const SYNTHETICS_DISABLE_FAILURE = i18n.translate( - 'xpack.uptime.monitorManagement.syntheticsDisabledFailure', + 'xpack.synthetics.monitorManagement.syntheticsDisabledFailure', { defaultMessage: 'Monitor Management was not able to be disabled. Please contact support.', } ); export const SYNTHETICS_ENABLE_SUCCESS = i18n.translate( - 'xpack.uptime.monitorManagement.syntheticsEnableSuccess', + 'xpack.synthetics.monitorManagement.syntheticsEnableSuccess', { defaultMessage: 'Monitor Management enabled successfully.', } ); export const SYNTHETICS_DISABLE_SUCCESS = i18n.translate( - 'xpack.uptime.monitorManagement.syntheticsDisabledSuccess', + 'xpack.synthetics.monitorManagement.syntheticsDisabledSuccess', { defaultMessage: 'Monitor Management disabled successfully.', } diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/hooks/use_run_once_errors.ts b/x-pack/plugins/synthetics/public/components/monitor_management/hooks/use_run_once_errors.ts index 52218986744c..204096f7e018 100644 --- a/x-pack/plugins/synthetics/public/components/monitor_management/hooks/use_run_once_errors.ts +++ b/x-pack/plugins/synthetics/public/components/monitor_management/hooks/use_run_once_errors.ts @@ -80,20 +80,20 @@ export function useRunOnceErrors({ }; } -const PushErrorLabel = i18n.translate('xpack.uptime.testRun.pushErrorLabel', { +const PushErrorLabel = i18n.translate('xpack.synthetics.testRun.pushErrorLabel', { defaultMessage: 'Push error', }); -const RunErrorLabel = i18n.translate('xpack.uptime.testRun.runErrorLabel', { +const RunErrorLabel = i18n.translate('xpack.synthetics.testRun.runErrorLabel', { defaultMessage: 'Error running test', }); const getLocationTestErrorLabel = (locationName: string) => - i18n.translate('xpack.uptime.testRun.runErrorLocation', { + i18n.translate('xpack.synthetics.testRun.runErrorLocation', { defaultMessage: 'Failed to run monitor on location {locationName}.', values: { locationName }, }); -const PushErrorService = i18n.translate('xpack.uptime.testRun.pushError', { +const PushErrorService = i18n.translate('xpack.synthetics.testRun.pushError', { defaultMessage: 'Failed to push the monitor to service.', }); diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_config/locations.tsx b/x-pack/plugins/synthetics/public/components/monitor_management/monitor_config/locations.tsx index 3d20e54aa3a3..fff6e11890f1 100644 --- a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_config/locations.tsx +++ b/x-pack/plugins/synthetics/public/components/monitor_management/monitor_config/locations.tsx @@ -70,14 +70,14 @@ export const ServiceLocations = ({ selectedLocations, setLocations, isInvalid, o }; const VALIDATION_ERROR = i18n.translate( - 'xpack.uptime.monitorManagement.serviceLocationsValidationError', + 'xpack.synthetics.monitorManagement.serviceLocationsValidationError', { defaultMessage: 'At least one service location must be specified', } ); export const LOCATIONS_LABEL = i18n.translate( - 'xpack.uptime.monitorManagement.monitorLocationsLabel', + 'xpack.synthetics.monitorManagement.monitorLocationsLabel', { defaultMessage: 'Monitor locations', } diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_config/monitor_advanced_fields.tsx b/x-pack/plugins/synthetics/public/components/monitor_management/monitor_config/monitor_advanced_fields.tsx index 5fdae73a43e1..086efe36ff11 100644 --- a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_config/monitor_advanced_fields.tsx +++ b/x-pack/plugins/synthetics/public/components/monitor_management/monitor_config/monitor_advanced_fields.tsx @@ -35,14 +35,14 @@ export const MonitorManagementAdvancedFields = memo( title={

} description={ } @@ -54,13 +54,13 @@ export const MonitorManagementAdvancedFields = memo( error={namespaceErrorMsg} label={ } helpText={ ( external > diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_config/monitor_config.tsx b/x-pack/plugins/synthetics/public/components/monitor_management/monitor_config/monitor_config.tsx index c2e828fe3321..8b62b7cde145 100644 --- a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_config/monitor_config.tsx +++ b/x-pack/plugins/synthetics/public/components/monitor_management/monitor_config/monitor_config.tsx @@ -111,10 +111,10 @@ export const MonitorConfig = ({ isEdit = false }: { isEdit: boolean }) => { ); }; -const TEST_RESULT = i18n.translate('xpack.uptime.monitorManagement.testResult', { +const TEST_RESULT = i18n.translate('xpack.synthetics.monitorManagement.testResult', { defaultMessage: 'Test result', }); -const CLOSE_LABEL = i18n.translate('xpack.uptime.monitorManagement.closeButtonLabel', { +const CLOSE_LABEL = i18n.translate('xpack.synthetics.monitorManagement.closeButtonLabel', { defaultMessage: 'Close', }); diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_config/monitor_name_location.tsx b/x-pack/plugins/synthetics/public/components/monitor_management/monitor_config/monitor_name_location.tsx index 8e8df803c7d7..e49a9b3e0483 100644 --- a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_config/monitor_name_location.tsx +++ b/x-pack/plugins/synthetics/public/components/monitor_management/monitor_config/monitor_name_location.tsx @@ -40,7 +40,7 @@ export const MonitorNameAndLocation = ({ validate, onFieldBlur }: Props) => { } @@ -51,7 +51,7 @@ export const MonitorNameAndLocation = ({ validate, onFieldBlur }: Props) => { NAME_ALREADY_EXISTS ) : ( ) @@ -79,6 +79,9 @@ export const MonitorNameAndLocation = ({ validate, onFieldBlur }: Props) => { ); }; -const NAME_ALREADY_EXISTS = i18n.translate('xpack.uptime.monitorManagement.duplicateNameError', { - defaultMessage: 'Monitor name already exists.', -}); +const NAME_ALREADY_EXISTS = i18n.translate( + 'xpack.synthetics.monitorManagement.duplicateNameError', + { + defaultMessage: 'Monitor name already exists.', + } +); diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/actions.tsx b/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/actions.tsx index 47a0b8547ea8..c290e58dc622 100644 --- a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/actions.tsx +++ b/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/actions.tsx @@ -62,6 +62,6 @@ export const Actions = ({ id, name, onUpdate, isDisabled, errorSummaries, monito ); }; -const EDIT_MONITOR_LABEL = i18n.translate('xpack.uptime.monitorManagement.editMonitorLabel', { +const EDIT_MONITOR_LABEL = i18n.translate('xpack.synthetics.monitorManagement.editMonitorLabel', { defaultMessage: 'Edit monitor', }); diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/delete_monitor.tsx b/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/delete_monitor.tsx index 0a90e4eb39a0..0feac678e651 100644 --- a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/delete_monitor.tsx +++ b/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/delete_monitor.tsx @@ -103,27 +103,30 @@ export const DeleteMonitor = ({ }; const DELETE_DESCRIPTION_LABEL = i18n.translate( - 'xpack.uptime.monitorManagement.confirmDescriptionLabel', + 'xpack.synthetics.monitorManagement.confirmDescriptionLabel', { defaultMessage: 'This action will delete the monitor but keep any data collected. This action cannot be undone.', } ); -const YES_LABEL = i18n.translate('xpack.uptime.monitorManagement.yesLabel', { +const YES_LABEL = i18n.translate('xpack.synthetics.monitorManagement.yesLabel', { defaultMessage: 'Delete', }); -const NO_LABEL = i18n.translate('xpack.uptime.monitorManagement.noLabel', { +const NO_LABEL = i18n.translate('xpack.synthetics.monitorManagement.noLabel', { defaultMessage: 'Cancel', }); -const DELETE_MONITOR_LABEL = i18n.translate('xpack.uptime.monitorManagement.deleteMonitorLabel', { - defaultMessage: 'Delete monitor', -}); +const DELETE_MONITOR_LABEL = i18n.translate( + 'xpack.synthetics.monitorManagement.deleteMonitorLabel', + { + defaultMessage: 'Delete monitor', + } +); const MONITOR_DELETE_SUCCESS_LABEL = i18n.translate( - 'xpack.uptime.monitorManagement.monitorDeleteSuccessMessage', + 'xpack.synthetics.monitorManagement.monitorDeleteSuccessMessage', { defaultMessage: 'Monitor deleted successfully.', } @@ -131,14 +134,14 @@ const MONITOR_DELETE_SUCCESS_LABEL = i18n.translate( // TODO: Discuss error states with product const MONITOR_DELETE_FAILURE_LABEL = i18n.translate( - 'xpack.uptime.monitorManagement.monitorDeleteFailureMessage', + 'xpack.synthetics.monitorManagement.monitorDeleteFailureMessage', { defaultMessage: 'Monitor was unable to be deleted. Please try again later.', } ); const MONITOR_DELETE_LOADING_LABEL = i18n.translate( - 'xpack.uptime.monitorManagement.monitorDeleteLoadingMessage', + 'xpack.synthetics.monitorManagement.monitorDeleteLoadingMessage', { defaultMessage: 'Deleting monitor...', } diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/enablement_empty_state.tsx b/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/enablement_empty_state.tsx index 0e7916f44a3a..745ec636af6b 100644 --- a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/enablement_empty_state.tsx +++ b/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/enablement_empty_state.tsx @@ -88,21 +88,21 @@ export const EnablementEmptyState = ({ focusButton }: { focusButton: boolean }) }; const MONITOR_MANAGEMENT_ENABLEMENT_LABEL = i18n.translate( - 'xpack.uptime.monitorManagement.emptyState.enablement.enabled.title', + 'xpack.synthetics.monitorManagement.emptyState.enablement.enabled.title', { defaultMessage: 'Enable Monitor Management', } ); const MONITOR_MANAGEMENT_DISABLED_LABEL = i18n.translate( - 'xpack.uptime.monitorManagement.emptyState.enablement.disabled.title', + 'xpack.synthetics.monitorManagement.emptyState.enablement.disabled.title', { defaultMessage: 'Monitor Management is disabled', } ); const MONITOR_MANAGEMENT_ENABLEMENT_MESSAGE = i18n.translate( - 'xpack.uptime.monitorManagement.emptyState.enablement', + 'xpack.synthetics.monitorManagement.emptyState.enablement', { defaultMessage: 'Enable Monitor Management to run lightweight and real-browser monitors from hosted testing locations around the world. Enabling Monitor Management will generate an API key to allow the Synthetics Service to write back to your Elasticsearch cluster.', @@ -110,7 +110,7 @@ const MONITOR_MANAGEMENT_ENABLEMENT_MESSAGE = i18n.translate( ); const MONITOR_MANAGEMENT_DISABLED_MESSAGE = i18n.translate( - 'xpack.uptime.monitorManagement.emptyState.enablement.disabledDescription', + 'xpack.synthetics.monitorManagement.emptyState.enablement.disabledDescription', { defaultMessage: 'Monitor Management is currently disabled. Monitor Management allows you to run lightweight and real-browser monitors from hosted testing locations around the world. To enable Monitor Management, please contact an administrator.', @@ -118,18 +118,18 @@ const MONITOR_MANAGEMENT_DISABLED_MESSAGE = i18n.translate( ); const MONITOR_MANAGEMENT_ENABLEMENT_BTN_LABEL = i18n.translate( - 'xpack.uptime.monitorManagement.emptyState.enablement.title', + 'xpack.synthetics.monitorManagement.emptyState.enablement.title', { defaultMessage: 'Enable', } ); -const DOCS_LABEL = i18n.translate('xpack.uptime.monitorManagement.emptyState.enablement.doc', { +const DOCS_LABEL = i18n.translate('xpack.synthetics.monitorManagement.emptyState.enablement.doc', { defaultMessage: 'Read the docs', }); const LEARN_MORE_LABEL = i18n.translate( - 'xpack.uptime.monitorManagement.emptyState.enablement.learnMore', + 'xpack.synthetics.monitorManagement.emptyState.enablement.learnMore', { defaultMessage: 'Want to learn more?', } diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/inline_error.tsx b/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/inline_error.tsx index 187c81ff8c6e..d9f47802e6c2 100644 --- a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/inline_error.tsx +++ b/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/inline_error.tsx @@ -40,12 +40,15 @@ export const InlineError = ({ errorSummary }: { errorSummary: Ping }) => { }; export const getInlineErrorLabel = (message?: string) => { - return i18n.translate('xpack.uptime.monitorList.statusColumn.error.message', { + return i18n.translate('xpack.synthetics.monitorList.statusColumn.error.message', { defaultMessage: '{message}. Click for more details.', values: { message }, }); }; -export const ERROR_LOGS_LABEL = i18n.translate('xpack.uptime.monitorList.statusColumn.error.logs', { - defaultMessage: 'Error logs', -}); +export const ERROR_LOGS_LABEL = i18n.translate( + 'xpack.synthetics.monitorList.statusColumn.error.logs', + { + defaultMessage: 'Error logs', + } +); diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/list_tabs.tsx b/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/list_tabs.tsx index 17b7ca839dcd..15012751af3e 100644 --- a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/list_tabs.tsx +++ b/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/list_tabs.tsx @@ -114,14 +114,17 @@ export const MonitorListTabs = ({ ); }; -export const REFRESH_LABEL = i18n.translate('xpack.uptime.monitorList.refresh', { +export const REFRESH_LABEL = i18n.translate('xpack.synthetics.monitorList.refresh', { defaultMessage: 'Refresh', }); -export const INVALID_MONITORS_LABEL = i18n.translate('xpack.uptime.monitorList.invalidMonitors', { - defaultMessage: 'Invalid monitors', -}); +export const INVALID_MONITORS_LABEL = i18n.translate( + 'xpack.synthetics.monitorList.invalidMonitors', + { + defaultMessage: 'Invalid monitors', + } +); -export const ALL_MONITORS_LABEL = i18n.translate('xpack.uptime.monitorList.allMonitors', { +export const ALL_MONITORS_LABEL = i18n.translate('xpack.synthetics.monitorList.allMonitors', { defaultMessage: 'All monitors', }); diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/monitor_async_error.tsx b/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/monitor_async_error.tsx index 48aeaf3648a0..77659c8357e2 100644 --- a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/monitor_async_error.tsx +++ b/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/monitor_async_error.tsx @@ -22,7 +22,7 @@ export const MonitorAsyncError = () => { } @@ -31,7 +31,7 @@ export const MonitorAsyncError = () => { >

@@ -58,28 +58,28 @@ export const MonitorAsyncError = () => { }; const REASON_LABEL = i18n.translate( - 'xpack.uptime.monitorManagement.monitorSync.failure.reasonLabel', + 'xpack.synthetics.monitorManagement.monitorSync.failure.reasonLabel', { defaultMessage: 'Reason', } ); const STATUS_LABEL = i18n.translate( - 'xpack.uptime.monitorManagement.monitorSync.failure.statusLabel', + 'xpack.synthetics.monitorManagement.monitorSync.failure.statusLabel', { defaultMessage: 'Status', } ); const NOT_AVAILABLE_LABEL = i18n.translate( - 'xpack.uptime.monitorManagement.monitorSync.failure.notAvailable', + 'xpack.synthetics.monitorManagement.monitorSync.failure.notAvailable', { defaultMessage: 'Not available', } ); const DISMISS_LABEL = i18n.translate( - 'xpack.uptime.monitorManagement.monitorSync.failure.dismissLabel', + 'xpack.synthetics.monitorManagement.monitorSync.failure.dismissLabel', { defaultMessage: 'Dismiss', } diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/monitor_enabled.tsx b/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/monitor_enabled.tsx index e441e44da221..6bbad58767b2 100644 --- a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/monitor_enabled.tsx +++ b/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/monitor_enabled.tsx @@ -87,28 +87,34 @@ export const MonitorEnabled = ({ id, monitor, onUpdate, isDisabled }: Props) => ); }; -const ENABLE_MONITOR_LABEL = i18n.translate('xpack.uptime.monitorManagement.enableMonitorLabel', { - defaultMessage: 'Enable monitor', -}); +const ENABLE_MONITOR_LABEL = i18n.translate( + 'xpack.synthetics.monitorManagement.enableMonitorLabel', + { + defaultMessage: 'Enable monitor', + } +); -const DISABLE_MONITOR_LABEL = i18n.translate('xpack.uptime.monitorManagement.disableMonitorLabel', { - defaultMessage: 'Disable monitor', -}); +const DISABLE_MONITOR_LABEL = i18n.translate( + 'xpack.synthetics.monitorManagement.disableMonitorLabel', + { + defaultMessage: 'Disable monitor', + } +); const getMonitorEnabledSuccessLabel = (name: string) => - i18n.translate('xpack.uptime.monitorManagement.monitorEnabledSuccessMessage', { + i18n.translate('xpack.synthetics.monitorManagement.monitorEnabledSuccessMessage', { defaultMessage: 'Monitor {name} enabled successfully.', values: { name }, }); const getMonitorDisabledSuccessLabel = (name: string) => - i18n.translate('xpack.uptime.monitorManagement.monitorDisabledSuccessMessage', { + i18n.translate('xpack.synthetics.monitorManagement.monitorDisabledSuccessMessage', { defaultMessage: 'Monitor {name} disabled successfully.', values: { name }, }); const getMonitorEnabledUpdateFailureMessage = (name: string) => - i18n.translate('xpack.uptime.monitorManagement.monitorEnabledUpdateFailureMessage', { + i18n.translate('xpack.synthetics.monitorManagement.monitorEnabledUpdateFailureMessage', { defaultMessage: 'Unable to update monitor {name}.', values: { name }, }); diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/monitor_list.tsx b/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/monitor_list.tsx index e38dad1c10d4..6dade5a1e418 100644 --- a/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/monitor_list.tsx +++ b/x-pack/plugins/synthetics/public/components/monitor_management/monitor_list/monitor_list.tsx @@ -115,7 +115,7 @@ export const MonitorManagementList = ({ { align: 'left' as const, field: ConfigKey.NAME as string, - name: i18n.translate('xpack.uptime.monitorManagement.monitorList.monitorName', { + name: i18n.translate('xpack.synthetics.monitorManagement.monitorList.monitorName', { defaultMessage: 'Monitor name', }), sortable: true, @@ -130,7 +130,7 @@ export const MonitorManagementList = ({ { align: 'left' as const, field: ConfigKey.MONITOR_TYPE, - name: i18n.translate('xpack.uptime.monitorManagement.monitorList.monitorType', { + name: i18n.translate('xpack.synthetics.monitorManagement.monitorList.monitorType', { defaultMessage: 'Monitor type', }), sortable: true, @@ -138,7 +138,7 @@ export const MonitorManagementList = ({ { align: 'left' as const, field: ConfigKey.TAGS, - name: i18n.translate('xpack.uptime.monitorManagement.monitorList.tags', { + name: i18n.translate('xpack.synthetics.monitorManagement.monitorList.tags', { defaultMessage: 'Tags', }), render: (tags: string[]) => (tags ? : null), @@ -146,7 +146,7 @@ export const MonitorManagementList = ({ { align: 'left' as const, field: ConfigKey.LOCATIONS, - name: i18n.translate('xpack.uptime.monitorManagement.monitorList.locations', { + name: i18n.translate('xpack.synthetics.monitorManagement.monitorList.locations', { defaultMessage: 'Locations', }), render: (locations: ServiceLocations) => @@ -155,7 +155,7 @@ export const MonitorManagementList = ({ { align: 'left' as const, field: ConfigKey.SCHEDULE, - name: i18n.translate('xpack.uptime.monitorManagement.monitorList.schedule', { + name: i18n.translate('xpack.synthetics.monitorManagement.monitorList.schedule', { defaultMessage: 'Frequency (min)', }), render: (schedule: CommonFields[ConfigKey.SCHEDULE]) => schedule?.number, @@ -163,7 +163,7 @@ export const MonitorManagementList = ({ { align: 'left' as const, field: ConfigKey.URLS, - name: i18n.translate('xpack.uptime.monitorManagement.monitorList.URL', { + name: i18n.translate('xpack.synthetics.monitorManagement.monitorList.URL', { defaultMessage: 'URL', }), sortable: true, @@ -174,7 +174,7 @@ export const MonitorManagementList = ({ { align: 'left' as const, field: ConfigKey.ENABLED as string, - name: i18n.translate('xpack.uptime.monitorManagement.monitorList.enabled', { + name: i18n.translate('xpack.synthetics.monitorManagement.monitorList.enabled', { defaultMessage: 'Enabled', }), render: (_enabled: boolean, monitor: EncryptedSyntheticsMonitorWithId) => ( @@ -188,7 +188,7 @@ export const MonitorManagementList = ({ }, { align: 'left' as const, - name: i18n.translate('xpack.uptime.monitorManagement.monitorList.actions', { + name: i18n.translate('xpack.synthetics.monitorManagement.monitorList.actions', { defaultMessage: 'Actions', }), render: (fields: EncryptedSyntheticsMonitorWithId) => ( @@ -208,7 +208,7 @@ export const MonitorManagementList = ({ { - return i18n.translate('xpack.uptime.monitorList.statusColumn.error.messageLabel', { + return i18n.translate('xpack.synthetics.monitorList.statusColumn.error.messageLabel', { defaultMessage: '{message}. Click for more details.', values: { message }, }); }; -export const ERROR_LOGS_LABEL = i18n.translate('xpack.uptime.monitorList.statusColumn.error.logs', { - defaultMessage: 'Error logs', -}); +export const ERROR_LOGS_LABEL = i18n.translate( + 'xpack.synthetics.monitorList.statusColumn.error.logs', + { + defaultMessage: 'Error logs', + } +); diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/show_sync_errors.tsx b/x-pack/plugins/synthetics/public/components/monitor_management/show_sync_errors.tsx index 2a6512b7b7df..0ce1fdd0d768 100644 --- a/x-pack/plugins/synthetics/public/components/monitor_management/show_sync_errors.tsx +++ b/x-pack/plugins/synthetics/public/components/monitor_management/show_sync_errors.tsx @@ -15,13 +15,13 @@ export const showSyncErrors = (errors: ServiceLocationErrors, locations: Service Object.values(errors).forEach((location) => { const { status: responseStatus, reason } = location.error || {}; kibanaService.toasts.addWarning({ - title: i18n.translate('xpack.uptime.monitorManagement.service.error.title', { + title: i18n.translate('xpack.synthetics.monitorManagement.service.error.title', { defaultMessage: `Unable to sync monitor config`, }), text: toMountPoint( <>

- {i18n.translate('xpack.uptime.monitorManagement.service.error.message', { + {i18n.translate('xpack.synthetics.monitorManagement.service.error.message', { defaultMessage: `Your monitor was saved, but there was a problem syncing the configuration for {location}. We will automatically try again later. If this problem continues, your monitors will stop running in {location}. Please contact Support for assistance.`, values: { location: locations?.find((loc) => loc?.id === location.locationId)?.label, @@ -31,13 +31,13 @@ export const showSyncErrors = (errors: ServiceLocationErrors, locations: Service {responseStatus || reason ? (

{responseStatus - ? i18n.translate('xpack.uptime.monitorManagement.service.error.status', { + ? i18n.translate('xpack.synthetics.monitorManagement.service.error.status', { defaultMessage: 'Status: {status}. ', values: { status: responseStatus }, }) : null} {reason - ? i18n.translate('xpack.uptime.monitorManagement.service.error.reason', { + ? i18n.translate('xpack.synthetics.monitorManagement.service.error.reason', { defaultMessage: 'Reason: {reason}.', values: { reason }, }) diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/test_now_mode/browser/browser_test_results.tsx b/x-pack/plugins/synthetics/public/components/monitor_management/test_now_mode/browser/browser_test_results.tsx index e5ca115b09b9..87eecddac3e6 100644 --- a/x-pack/plugins/synthetics/public/components/monitor_management/test_now_mode/browser/browser_test_results.tsx +++ b/x-pack/plugins/synthetics/public/components/monitor_management/test_now_mode/browser/browser_test_results.tsx @@ -115,7 +115,7 @@ function getButtonContent({

- {i18n.translate('xpack.uptime.monitorManagement.stepCompleted', { + {i18n.translate('xpack.synthetics.monitorManagement.stepCompleted', { defaultMessage: '{stepCount, number} {stepCount, plural, one {step} other {steps}} completed', values: { @@ -129,10 +129,10 @@ function getButtonContent({ ); } -const FAILED_TO_RUN = i18n.translate('xpack.uptime.monitorManagement.failedRun', { +const FAILED_TO_RUN = i18n.translate('xpack.synthetics.monitorManagement.failedRun', { defaultMessage: 'Failed to run steps', }); -const LOADING_STEPS = i18n.translate('xpack.uptime.monitorManagement.loadingSteps', { +const LOADING_STEPS = i18n.translate('xpack.synthetics.monitorManagement.loadingSteps', { defaultMessage: 'Loading steps...', }); diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/test_now_mode/test_now_mode.tsx b/x-pack/plugins/synthetics/public/components/monitor_management/test_now_mode/test_now_mode.tsx index 0cc5f0658ff4..e89298c9aa46 100644 --- a/x-pack/plugins/synthetics/public/components/monitor_management/test_now_mode/test_now_mode.tsx +++ b/x-pack/plugins/synthetics/public/components/monitor_management/test_now_mode/test_now_mode.tsx @@ -111,6 +111,6 @@ export function TestNowMode({ ); } -const PushingLabel = i18n.translate('xpack.uptime.testRun.pushing.description', { +const PushingLabel = i18n.translate('xpack.synthetics.testRun.pushing.description', { defaultMessage: 'Pushing the monitor to service...', }); diff --git a/x-pack/plugins/synthetics/public/components/monitor_management/test_now_mode/test_result_header.tsx b/x-pack/plugins/synthetics/public/components/monitor_management/test_now_mode/test_result_header.tsx index 7c53e99a6e9a..62c1971d87ed 100644 --- a/x-pack/plugins/synthetics/public/components/monitor_management/test_now_mode/test_result_header.tsx +++ b/x-pack/plugins/synthetics/public/components/monitor_management/test_now_mode/test_result_header.tsx @@ -62,7 +62,7 @@ export function TestResultHeader({ - {i18n.translate('xpack.uptime.monitorManagement.timeTaken', { + {i18n.translate('xpack.synthetics.monitorManagement.timeTaken', { defaultMessage: 'Took {timeTaken}', values: { timeTaken: formatDuration(duration) }, })} @@ -93,26 +93,26 @@ export function TestResultHeader({ ); } -export const PENDING_LABEL = i18n.translate('xpack.uptime.monitorManagement.pending', { +export const PENDING_LABEL = i18n.translate('xpack.synthetics.monitorManagement.pending', { defaultMessage: 'PENDING', }); -const TEST_RESULT = i18n.translate('xpack.uptime.monitorManagement.testResult', { +const TEST_RESULT = i18n.translate('xpack.synthetics.monitorManagement.testResult', { defaultMessage: 'Test result', }); -const COMPLETED_LABEL = i18n.translate('xpack.uptime.monitorManagement.completed', { +const COMPLETED_LABEL = i18n.translate('xpack.synthetics.monitorManagement.completed', { defaultMessage: 'COMPLETED', }); -const FAILED_LABEL = i18n.translate('xpack.uptime.monitorManagement.failed', { +const FAILED_LABEL = i18n.translate('xpack.synthetics.monitorManagement.failed', { defaultMessage: 'FAILED', }); -export const IN_PROGRESS_LABEL = i18n.translate('xpack.uptime.monitorManagement.inProgress', { +export const IN_PROGRESS_LABEL = i18n.translate('xpack.synthetics.monitorManagement.inProgress', { defaultMessage: 'IN PROGRESS', }); -const VIEW_DETAILS = i18n.translate('xpack.uptime.monitorManagement.viewTestRunDetails', { +const VIEW_DETAILS = i18n.translate('xpack.synthetics.monitorManagement.viewTestRunDetails', { defaultMessage: 'View test result details', }); diff --git a/x-pack/plugins/synthetics/public/components/overview/alerts/alert_query_bar/query_bar.tsx b/x-pack/plugins/synthetics/public/components/overview/alerts/alert_query_bar/query_bar.tsx index 0e9244cdc15a..b990fb70a224 100644 --- a/x-pack/plugins/synthetics/public/components/overview/alerts/alert_query_bar/query_bar.tsx +++ b/x-pack/plugins/synthetics/public/components/overview/alerts/alert_query_bar/query_bar.tsx @@ -46,11 +46,11 @@ export const AlertQueryBar = ({ query = '', onChange }: Props) => { }} query={{ query: inputVal, language: 'kuery' }} aria-label={labels.ALERT_KUERY_BAR_ARIA} - dataTestSubj="xpack.uptime.alerts.monitorStatus.filterBar" + dataTestSubj="xpack.synthetics.alerts.monitorStatus.filterBar" autoSubmit={true} disableLanguageSwitcher={true} isInvalid={!!(inputVal && !query)} - placeholder={i18n.translate('xpack.uptime.alerts.searchPlaceholder.kql', { + placeholder={i18n.translate('xpack.synthetics.alerts.searchPlaceholder.kql', { defaultMessage: 'Filter using kql syntax', })} /> diff --git a/x-pack/plugins/synthetics/public/components/overview/alerts/anomaly_alert/select_severity.tsx b/x-pack/plugins/synthetics/public/components/overview/alerts/anomaly_alert/select_severity.tsx index eed650fe6074..a1c6b88042b7 100644 --- a/x-pack/plugins/synthetics/public/components/overview/alerts/anomaly_alert/select_severity.tsx +++ b/x-pack/plugins/synthetics/public/components/overview/alerts/anomaly_alert/select_severity.tsx @@ -12,16 +12,16 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { EuiHealth, EuiSpacer, EuiSuperSelect, EuiText } from '@elastic/eui'; import { getSeverityColor } from '@kbn/ml-plugin/public'; -const warningLabel = i18n.translate('xpack.uptime.controls.selectSeverity.warningLabel', { +const warningLabel = i18n.translate('xpack.synthetics.controls.selectSeverity.warningLabel', { defaultMessage: 'warning', }); -const minorLabel = i18n.translate('xpack.uptime.controls.selectSeverity.minorLabel', { +const minorLabel = i18n.translate('xpack.synthetics.controls.selectSeverity.minorLabel', { defaultMessage: 'minor', }); -const majorLabel = i18n.translate('xpack.uptime.controls.selectSeverity.majorLabel', { +const majorLabel = i18n.translate('xpack.synthetics.controls.selectSeverity.majorLabel', { defaultMessage: 'major', }); -const criticalLabel = i18n.translate('xpack.uptime.controls.selectSeverity.criticalLabel', { +const criticalLabel = i18n.translate('xpack.synthetics.controls.selectSeverity.criticalLabel', { defaultMessage: 'critical', }); @@ -95,7 +95,7 @@ const getSeverityOptions = () =>

diff --git a/x-pack/plugins/synthetics/public/components/overview/alerts/anomaly_alert/translations.ts b/x-pack/plugins/synthetics/public/components/overview/alerts/anomaly_alert/translations.ts index 1249c66f92cb..ea9aac0ddb48 100644 --- a/x-pack/plugins/synthetics/public/components/overview/alerts/anomaly_alert/translations.ts +++ b/x-pack/plugins/synthetics/public/components/overview/alerts/anomaly_alert/translations.ts @@ -8,17 +8,20 @@ import { i18n } from '@kbn/i18n'; export const AnomalyTranslations = { - criteriaAriaLabel: i18n.translate('xpack.uptime.alerts.anomaly.criteriaExpression.ariaLabel', { - defaultMessage: 'An expression displaying the criteria for a selected monitor.', - }), - whenMonitor: i18n.translate('xpack.uptime.alerts.anomaly.criteriaExpression.description', { + criteriaAriaLabel: i18n.translate( + 'xpack.synthetics.alerts.anomaly.criteriaExpression.ariaLabel', + { + defaultMessage: 'An expression displaying the criteria for a selected monitor.', + } + ), + whenMonitor: i18n.translate('xpack.synthetics.alerts.anomaly.criteriaExpression.description', { defaultMessage: 'When monitor', }), - scoreAriaLabel: i18n.translate('xpack.uptime.alerts.anomaly.scoreExpression.ariaLabel', { + scoreAriaLabel: i18n.translate('xpack.synthetics.alerts.anomaly.scoreExpression.ariaLabel', { defaultMessage: 'An expression displaying the criteria for an anomaly alert threshold.', }), hasAnomalyWithSeverity: i18n.translate( - 'xpack.uptime.alerts.anomaly.scoreExpression.description', + 'xpack.synthetics.alerts.anomaly.scoreExpression.description', { defaultMessage: 'has anomaly with severity', description: 'An expression displaying the criteria for an anomaly alert threshold.', diff --git a/x-pack/plugins/synthetics/public/components/overview/alerts/monitor_expressions/__snapshots__/down_number_select.test.tsx.snap b/x-pack/plugins/synthetics/public/components/overview/alerts/monitor_expressions/__snapshots__/down_number_select.test.tsx.snap index f9e169b976d1..d22c2fb1bad4 100644 --- a/x-pack/plugins/synthetics/public/components/overview/alerts/monitor_expressions/__snapshots__/down_number_select.test.tsx.snap +++ b/x-pack/plugins/synthetics/public/components/overview/alerts/monitor_expressions/__snapshots__/down_number_select.test.tsx.snap @@ -11,7 +11,7 @@ exports[`DownNoExpressionSelect component should renders against props 1`] = ` } - data-test-subj="xpack.uptime.alerts.monitorStatus.numTimesExpression" + data-test-subj="xpack.synthetics.alerts.monitorStatus.numTimesExpression" description="matching monitors are down >" id="ping-count" value="5 times" diff --git a/x-pack/plugins/synthetics/public/components/overview/alerts/monitor_expressions/__snapshots__/time_expression_select.test.tsx.snap b/x-pack/plugins/synthetics/public/components/overview/alerts/monitor_expressions/__snapshots__/time_expression_select.test.tsx.snap index d7f04798198c..5ff6e5773485 100644 --- a/x-pack/plugins/synthetics/public/components/overview/alerts/monitor_expressions/__snapshots__/time_expression_select.test.tsx.snap +++ b/x-pack/plugins/synthetics/public/components/overview/alerts/monitor_expressions/__snapshots__/time_expression_select.test.tsx.snap @@ -17,7 +17,7 @@ exports[`TimeExpressionSelect component should renders against props 1`] = ` } - data-test-subj="xpack.uptime.alerts.monitorStatus.timerangeValueExpression" + data-test-subj="xpack.synthetics.alerts.monitorStatus.timerangeValueExpression" description="within" id="timerange" value="last 15" @@ -95,33 +95,33 @@ exports[`TimeExpressionSelect component should shallow renders against props 1`] content={ } - data-test-subj="xpack.uptime.alerts.monitorStatus.timerangeUnitExpression" + data-test-subj="xpack.synthetics.alerts.monitorStatus.timerangeUnitExpression" description="" id="timerange-unit" value="minutes" diff --git a/x-pack/plugins/synthetics/public/components/overview/alerts/monitor_expressions/availability_expression_select.tsx b/x-pack/plugins/synthetics/public/components/overview/alerts/monitor_expressions/availability_expression_select.tsx index b125189230ed..e6aef70beeaa 100644 --- a/x-pack/plugins/synthetics/public/components/overview/alerts/monitor_expressions/availability_expression_select.tsx +++ b/x-pack/plugins/synthetics/public/components/overview/alerts/monitor_expressions/availability_expression_select.tsx @@ -22,25 +22,28 @@ interface Props { const TimeRangeOptions: TimeRangeOption[] = [ { 'aria-label': labels.DAYS_TIME_RANGE, - 'data-test-subj': 'xpack.uptime.alerts.monitorStatus.availability.timerangeUnit.daysOption', + 'data-test-subj': 'xpack.synthetics.alerts.monitorStatus.availability.timerangeUnit.daysOption', key: 'd', label: labels.DAYS, }, { 'aria-label': labels.WEEKS_TIME_RANGE, - 'data-test-subj': 'xpack.uptime.alerts.monitorStatus.availability.timerangeUnit.weeksOption', + 'data-test-subj': + 'xpack.synthetics.alerts.monitorStatus.availability.timerangeUnit.weeksOption', key: 'w', label: labels.WEEKS, }, { 'aria-label': labels.MONTHS_TIME_RANGE, - 'data-test-subj': 'xpack.uptime.alerts.monitorStatus.availability.timerangeUnit.monthsOption', + 'data-test-subj': + 'xpack.synthetics.alerts.monitorStatus.availability.timerangeUnit.monthsOption', key: 'M', label: labels.MONTHS, }, { 'aria-label': labels.YEARS_TIME_RANGE, - 'data-test-subj': 'xpack.uptime.alerts.monitorStatus.availability.timerangeUnit.yearsOption', + 'data-test-subj': + 'xpack.synthetics.alerts.monitorStatus.availability.timerangeUnit.yearsOption', key: 'y', label: labels.YEARS, }, @@ -115,7 +118,7 @@ export const AvailabilityExpressionSelect: React.FC = ({ }} /> } - data-test-subj="xpack.uptime.alerts.monitorStatus.availability.threshold" + data-test-subj="xpack.synthetics.alerts.monitorStatus.availability.threshold" description={ hasFilters ? labels.ENTER_AVAILABILITY_THRESHOLD_DESCRIPTION @@ -135,13 +138,13 @@ export const AvailabilityExpressionSelect: React.FC = ({ content={ } - data-test-subj="xpack.uptime.alerts.monitorStatus.availability.timerangeExpression" + data-test-subj="xpack.synthetics.alerts.monitorStatus.availability.timerangeExpression" description={labels.ENTER_AVAILABILITY_RANGE_UNITS_DESCRIPTION} id="range" isEnabled={isEnabled} @@ -150,11 +153,11 @@ export const AvailabilityExpressionSelect: React.FC = ({ { @@ -168,7 +171,7 @@ export const AvailabilityExpressionSelect: React.FC = ({ timeRangeOptions={timerangeUnitOptions} /> } - data-test-subj="xpack.uptime.alerts.monitorStatus.availability.timerangeUnit" + data-test-subj="xpack.synthetics.alerts.monitorStatus.availability.timerangeUnit" description="" id="availability-unit" isEnabled={isEnabled} diff --git a/x-pack/plugins/synthetics/public/components/overview/alerts/monitor_expressions/down_number_select.tsx b/x-pack/plugins/synthetics/public/components/overview/alerts/monitor_expressions/down_number_select.tsx index a1d459f89f02..e1feaf2449fd 100644 --- a/x-pack/plugins/synthetics/public/components/overview/alerts/monitor_expressions/down_number_select.tsx +++ b/x-pack/plugins/synthetics/public/components/overview/alerts/monitor_expressions/down_number_select.tsx @@ -35,13 +35,13 @@ export const DownNoExpressionSelect: React.FC = ({ content={ } - data-test-subj="xpack.uptime.alerts.monitorStatus.numTimesExpression" + data-test-subj="xpack.synthetics.alerts.monitorStatus.numTimesExpression" description={hasFilters ? labels.MATCHING_MONITORS_DOWN : labels.ANY_MONITOR_DOWN} id="ping-count" isEnabled={isEnabled} diff --git a/x-pack/plugins/synthetics/public/components/overview/alerts/monitor_expressions/time_expression_select.tsx b/x-pack/plugins/synthetics/public/components/overview/alerts/monitor_expressions/time_expression_select.tsx index 957a28938656..f3729fd6ebfa 100644 --- a/x-pack/plugins/synthetics/public/components/overview/alerts/monitor_expressions/time_expression_select.tsx +++ b/x-pack/plugins/synthetics/public/components/overview/alerts/monitor_expressions/time_expression_select.tsx @@ -25,25 +25,25 @@ const DEFAULT_TIMERANGE_UNIT = 'm'; const TimeRangeOptions: TimeRangeOption[] = [ { 'aria-label': labels.SECONDS_TIME_RANGE, - 'data-test-subj': 'xpack.uptime.alerts.monitorStatus.timerangeUnitSelectable.secondsOption', + 'data-test-subj': 'xpack.synthetics.alerts.monitorStatus.timerangeUnitSelectable.secondsOption', key: 's', label: labels.SECONDS, }, { 'aria-label': labels.MINUTES_TIME_RANGE, - 'data-test-subj': 'xpack.uptime.alerts.monitorStatus.timerangeUnitSelectable.minutesOption', + 'data-test-subj': 'xpack.synthetics.alerts.monitorStatus.timerangeUnitSelectable.minutesOption', key: 'm', label: labels.MINUTES, }, { 'aria-label': labels.HOURS_TIME_RANGE, - 'data-test-subj': 'xpack.uptime.alerts.monitorStatus.timerangeUnitSelectable.hoursOption', + 'data-test-subj': 'xpack.synthetics.alerts.monitorStatus.timerangeUnitSelectable.hoursOption', key: 'h', label: labels.HOURS, }, { 'aria-label': labels.DAYS_TIME_RANGE, - 'data-test-subj': 'xpack.uptime.alerts.monitorStatus.timerangeUnitSelectable.daysOption', + 'data-test-subj': 'xpack.synthetics.alerts.monitorStatus.timerangeUnitSelectable.daysOption', key: 'd', label: labels.DAYS, }, @@ -77,13 +77,13 @@ export const TimeExpressionSelect: React.FC = ({ content={ } - data-test-subj="xpack.uptime.alerts.monitorStatus.timerangeValueExpression" + data-test-subj="xpack.synthetics.alerts.monitorStatus.timerangeValueExpression" description={labels.ENTER_NUMBER_OF_TIME_UNITS_DESCRIPTION} id="timerange" isEnabled={isEnabled} @@ -96,7 +96,7 @@ export const TimeExpressionSelect: React.FC = ({ content={ >) => { if (newOptions.reduce((acc, { checked }) => acc || checked === 'on', false)) { @@ -106,7 +106,7 @@ export const TimeExpressionSelect: React.FC = ({ timeRangeOptions={timerangeUnitOptions} /> } - data-test-subj="xpack.uptime.alerts.monitorStatus.timerangeUnitExpression" + data-test-subj="xpack.synthetics.alerts.monitorStatus.timerangeUnitExpression" description="" id="timerange-unit" isEnabled={isEnabled} diff --git a/x-pack/plugins/synthetics/public/components/overview/alerts/monitor_expressions/translations.ts b/x-pack/plugins/synthetics/public/components/overview/alerts/monitor_expressions/translations.ts index ca00aabc8b9b..05d736826a48 100644 --- a/x-pack/plugins/synthetics/public/components/overview/alerts/monitor_expressions/translations.ts +++ b/x-pack/plugins/synthetics/public/components/overview/alerts/monitor_expressions/translations.ts @@ -8,82 +8,85 @@ import { i18n } from '@kbn/i18n'; export const alertFilterLabels = { - USING: i18n.translate('xpack.uptime.alerts.monitorStatus.filters.using', { + USING: i18n.translate('xpack.synthetics.alerts.monitorStatus.filters.using', { defaultMessage: 'Using', }), - USING_PORT: i18n.translate('xpack.uptime.alerts.monitorStatus.filters.usingPort', { + USING_PORT: i18n.translate('xpack.synthetics.alerts.monitorStatus.filters.usingPort', { defaultMessage: 'Using port', }), - ANY_PORT: i18n.translate('xpack.uptime.alerts.monitorStatus.filters.anyPort', { + ANY_PORT: i18n.translate('xpack.synthetics.alerts.monitorStatus.filters.anyPort', { defaultMessage: 'any port', }), - WITH: i18n.translate('xpack.uptime.alerts.monitorStatus.filters.with', { + WITH: i18n.translate('xpack.synthetics.alerts.monitorStatus.filters.with', { defaultMessage: 'Using', }), - WITH_TAG: i18n.translate('xpack.uptime.alerts.monitorStatus.filters.withTag', { + WITH_TAG: i18n.translate('xpack.synthetics.alerts.monitorStatus.filters.withTag', { defaultMessage: 'With tag', }), - ANY_TAG: i18n.translate('xpack.uptime.alerts.monitorStatus.filters.anyTag', { + ANY_TAG: i18n.translate('xpack.synthetics.alerts.monitorStatus.filters.anyTag', { defaultMessage: 'any tag', }), - OF: i18n.translate('xpack.uptime.alerts.monitorStatus.filters.of', { + OF: i18n.translate('xpack.synthetics.alerts.monitorStatus.filters.of', { defaultMessage: 'Of', }), - OF_TYPE: i18n.translate('xpack.uptime.alerts.monitorStatus.filters.ofType', { + OF_TYPE: i18n.translate('xpack.synthetics.alerts.monitorStatus.filters.ofType', { defaultMessage: 'Of type', }), - ANY_TYPE: i18n.translate('xpack.uptime.alerts.monitorStatus.filters.anyType', { + ANY_TYPE: i18n.translate('xpack.synthetics.alerts.monitorStatus.filters.anyType', { defaultMessage: 'any type', }), - FROM: i18n.translate('xpack.uptime.alerts.monitorStatus.filters.from', { + FROM: i18n.translate('xpack.synthetics.alerts.monitorStatus.filters.from', { defaultMessage: 'From', }), - FROM_LOCATION: i18n.translate('xpack.uptime.alerts.monitorStatus.filters.fromLocation', { + FROM_LOCATION: i18n.translate('xpack.synthetics.alerts.monitorStatus.filters.fromLocation', { defaultMessage: 'From location', }), - ANY_LOCATION: i18n.translate('xpack.uptime.alerts.monitorStatus.filters.anyLocation', { + ANY_LOCATION: i18n.translate('xpack.synthetics.alerts.monitorStatus.filters.anyLocation', { defaultMessage: 'any location', }), REMOVE_FILTER_LABEL: (title: string) => - i18n.translate('xpack.uptime.alerts.monitorExpression.label', { + i18n.translate('xpack.synthetics.alerts.monitorExpression.label', { defaultMessage: 'Remove filter {title}', values: { title }, }), }; export const statusExpLabels = { - ENABLED_CHECKBOX: i18n.translate('xpack.uptime.alerts.monitorStatus.statusEnabledCheck.label', { - defaultMessage: 'Status check', - }), + ENABLED_CHECKBOX: i18n.translate( + 'xpack.synthetics.alerts.monitorStatus.statusEnabledCheck.label', + { + defaultMessage: 'Status check', + } + ), }; export const timeExpLabels = { OPEN_TIME_POPOVER: i18n.translate( - 'xpack.uptime.alerts.monitorStatus.timerangeUnitExpression.ariaLabel', + 'xpack.synthetics.alerts.monitorStatus.timerangeUnitExpression.ariaLabel', { defaultMessage: 'Open the popover for time range unit select field', } ), SELECT_TIME_RANGE_ARIA: i18n.translate( - 'xpack.uptime.alerts.monitorStatus.timerangeUnitSelectable', + 'xpack.synthetics.alerts.monitorStatus.timerangeUnitSelectable', { defaultMessage: 'Selectable field for the time range units alerts should use', } ), SELECT_TIME_RANGE_HEADLINE: i18n.translate( - 'xpack.uptime.alerts.monitorStatus.timerangeSelectionHeader', + 'xpack.synthetics.alerts.monitorStatus.timerangeSelectionHeader', { defaultMessage: 'Select time range unit', } @@ -91,16 +94,16 @@ export const timeExpLabels = { }; export const filterAriaLabels = { - PORT: i18n.translate('xpack.uptime.alerts.monitorStatus.filters.port.label', { + PORT: i18n.translate('xpack.synthetics.alerts.monitorStatus.filters.port.label', { defaultMessage: `Select port filters to apply to the alert's query.`, }), - TAG: i18n.translate('xpack.uptime.alerts.monitorStatus.filters.tag.label', { + TAG: i18n.translate('xpack.synthetics.alerts.monitorStatus.filters.tag.label', { defaultMessage: `Select tag filters to apply to the alert's query.`, }), - SCHEME: i18n.translate('xpack.uptime.alerts.monitorStatus.filters.scheme.label', { + SCHEME: i18n.translate('xpack.synthetics.alerts.monitorStatus.filters.scheme.label', { defaultMessage: `Select protocol scheme filters to apply to the alert's query.`, }), - LOCATION: i18n.translate('xpack.uptime.alerts.monitorStatus.filters.location.label', { + LOCATION: i18n.translate('xpack.synthetics.alerts.monitorStatus.filters.location.label', { defaultMessage: `Select location filters to apply to the alert's query.`, }), }; diff --git a/x-pack/plugins/synthetics/public/components/overview/alerts/monitor_status_alert/alert_monitor_status.tsx b/x-pack/plugins/synthetics/public/components/overview/alerts/monitor_status_alert/alert_monitor_status.tsx index 577df06802e4..1fa06458e3a8 100644 --- a/x-pack/plugins/synthetics/public/components/overview/alerts/monitor_status_alert/alert_monitor_status.tsx +++ b/x-pack/plugins/synthetics/public/components/overview/alerts/monitor_status_alert/alert_monitor_status.tsx @@ -71,7 +71,7 @@ export const AlertMonitorStatusComponent: React.FC = (p title={ {' '} diff --git a/x-pack/plugins/synthetics/public/components/overview/alerts/monitor_status_alert/old_alert_call_out.tsx b/x-pack/plugins/synthetics/public/components/overview/alerts/monitor_status_alert/old_alert_call_out.tsx index bce33bea4cca..d9d52496aaa8 100644 --- a/x-pack/plugins/synthetics/public/components/overview/alerts/monitor_status_alert/old_alert_call_out.tsx +++ b/x-pack/plugins/synthetics/public/components/overview/alerts/monitor_status_alert/old_alert_call_out.tsx @@ -23,7 +23,7 @@ export const OldAlertCallOut: React.FC = ({ isOldAlert }) => { size="s" title={ } diff --git a/x-pack/plugins/synthetics/public/components/overview/alerts/monitor_status_alert/old_alert_callout.test.tsx b/x-pack/plugins/synthetics/public/components/overview/alerts/monitor_status_alert/old_alert_callout.test.tsx index f0c41480f517..9145137b0d88 100644 --- a/x-pack/plugins/synthetics/public/components/overview/alerts/monitor_status_alert/old_alert_callout.test.tsx +++ b/x-pack/plugins/synthetics/public/components/overview/alerts/monitor_status_alert/old_alert_callout.test.tsx @@ -26,7 +26,7 @@ describe('OldAlertCallOut', () => { title={ } diff --git a/x-pack/plugins/synthetics/public/components/overview/alerts/settings_message_expression_popover.tsx b/x-pack/plugins/synthetics/public/components/overview/alerts/settings_message_expression_popover.tsx index 71cdee07699c..cf4775033443 100644 --- a/x-pack/plugins/synthetics/public/components/overview/alerts/settings_message_expression_popover.tsx +++ b/x-pack/plugins/synthetics/public/components/overview/alerts/settings_message_expression_popover.tsx @@ -31,7 +31,7 @@ export const SettingsMessageExpressionPopover: React.FC setIsOpen(false)} > + { setAlertFlyoutVisible(false); diff --git a/x-pack/plugins/synthetics/public/components/overview/alerts/toggle_alert_flyout_button.tsx b/x-pack/plugins/synthetics/public/components/overview/alerts/toggle_alert_flyout_button.tsx index 9aede506dde9..894c3df70a40 100644 --- a/x-pack/plugins/synthetics/public/components/overview/alerts/toggle_alert_flyout_button.tsx +++ b/x-pack/plugins/synthetics/public/components/overview/alerts/toggle_alert_flyout_button.tsx @@ -33,7 +33,7 @@ const ALERT_CONTEXT_MAIN_PANEL_ID = 0; const ALERT_CONTEXT_SELECT_TYPE_PANEL_ID = 1; const noWritePermissionsTooltipContent = i18n.translate( - 'xpack.uptime.alertDropdown.noWritePermissions', + 'xpack.synthetics.alertDropdown.noWritePermissions', { defaultMessage: 'You need read-write access to Uptime to create alerts in this app.', } @@ -53,7 +53,7 @@ export const ToggleAlertFlyoutButtonComponent: React.FC = ({ const monitorStatusAlertContextMenuItem: EuiContextMenuPanelItemDescriptor = { 'aria-label': ToggleFlyoutTranslations.toggleMonitorStatusAriaLabel, - 'data-test-subj': 'xpack.uptime.toggleAlertFlyout', + 'data-test-subj': 'xpack.synthetics.toggleAlertFlyout', name: ToggleFlyoutTranslations.toggleMonitorStatusContent, onClick: () => { setAlertFlyoutVisible(CLIENT_ALERT_TYPES.MONITOR_STATUS); @@ -63,7 +63,7 @@ export const ToggleAlertFlyoutButtonComponent: React.FC = ({ const tlsAlertContextMenuItem: EuiContextMenuPanelItemDescriptor = { 'aria-label': ToggleFlyoutTranslations.toggleTlsAriaLabel, - 'data-test-subj': 'xpack.uptime.toggleTlsAlertFlyout', + 'data-test-subj': 'xpack.synthetics.toggleTlsAlertFlyout', name: ToggleFlyoutTranslations.toggleTlsContent, onClick: () => { setAlertFlyoutVisible(CLIENT_ALERT_TYPES.TLS); @@ -73,11 +73,11 @@ export const ToggleAlertFlyoutButtonComponent: React.FC = ({ const managementContextItem: EuiContextMenuPanelItemDescriptor = { 'aria-label': ToggleFlyoutTranslations.navigateToAlertingUIAriaLabel, - 'data-test-subj': 'xpack.uptime.navigateToAlertingUi', + 'data-test-subj': 'xpack.synthetics.navigateToAlertingUi', name: ( @@ -115,7 +115,7 @@ export const ToggleAlertFlyoutButtonComponent: React.FC = ({ items: [ { 'aria-label': ToggleFlyoutTranslations.openAlertContextPanelAriaLabel, - 'data-test-subj': 'xpack.uptime.openAlertContextPanel', + 'data-test-subj': 'xpack.synthetics.openAlertContextPanel', name: ToggleFlyoutTranslations.openAlertContextPanelLabel, icon: 'bell', panel: ALERT_CONTEXT_SELECT_TYPE_PANEL_ID, @@ -139,13 +139,13 @@ export const ToggleAlertFlyoutButtonComponent: React.FC = ({ setIsOpen(!isOpen)} > diff --git a/x-pack/plugins/synthetics/public/components/overview/alerts/translations.ts b/x-pack/plugins/synthetics/public/components/overview/alerts/translations.ts index 76d8ebaea371..0580528b6b38 100644 --- a/x-pack/plugins/synthetics/public/components/overview/alerts/translations.ts +++ b/x-pack/plugins/synthetics/public/components/overview/alerts/translations.ts @@ -8,188 +8,197 @@ import { i18n } from '@kbn/i18n'; export const SECONDS_TIME_RANGE = i18n.translate( - 'xpack.uptime.alerts.timerangeUnitSelectable.secondsOption.ariaLabel', + 'xpack.synthetics.alerts.timerangeUnitSelectable.secondsOption.ariaLabel', { defaultMessage: '"Seconds" time range select item', } ); -export const SECONDS = i18n.translate('xpack.uptime.alerts.monitorStatus.timerangeOption.seconds', { - defaultMessage: 'seconds', -}); +export const SECONDS = i18n.translate( + 'xpack.synthetics.alerts.monitorStatus.timerangeOption.seconds', + { + defaultMessage: 'seconds', + } +); export const MINUTES_TIME_RANGE = i18n.translate( - 'xpack.uptime.alerts.timerangeUnitSelectable.minutesOption.ariaLabel', + 'xpack.synthetics.alerts.timerangeUnitSelectable.minutesOption.ariaLabel', { defaultMessage: '"Minutes" time range select item', } ); -export const MINUTES = i18n.translate('xpack.uptime.alerts.monitorStatus.timerangeOption.minutes', { - defaultMessage: 'minutes', -}); +export const MINUTES = i18n.translate( + 'xpack.synthetics.alerts.monitorStatus.timerangeOption.minutes', + { + defaultMessage: 'minutes', + } +); export const HOURS_TIME_RANGE = i18n.translate( - 'xpack.uptime.alerts.timerangeUnitSelectable.hoursOption.ariaLabel', + 'xpack.synthetics.alerts.timerangeUnitSelectable.hoursOption.ariaLabel', { defaultMessage: '"Hours" time range select item', } ); -export const HOURS = i18n.translate('xpack.uptime.alerts.monitorStatus.timerangeOption.hours', { +export const HOURS = i18n.translate('xpack.synthetics.alerts.monitorStatus.timerangeOption.hours', { defaultMessage: 'hours', }); export const DAYS_TIME_RANGE = i18n.translate( - 'xpack.uptime.alerts.timerangeUnitSelectable.daysOption.ariaLabel', + 'xpack.synthetics.alerts.timerangeUnitSelectable.daysOption.ariaLabel', { defaultMessage: '"Days" time range select item', } ); -export const DAYS = i18n.translate('xpack.uptime.alerts.monitorStatus.timerangeOption.days', { +export const DAYS = i18n.translate('xpack.synthetics.alerts.monitorStatus.timerangeOption.days', { defaultMessage: 'days', }); export const WEEKS_TIME_RANGE = i18n.translate( - 'xpack.uptime.alerts.timerangeUnitSelectable.weeksOption.ariaLabel', + 'xpack.synthetics.alerts.timerangeUnitSelectable.weeksOption.ariaLabel', { defaultMessage: '"Weeks" time range select item', } ); -export const WEEKS = i18n.translate('xpack.uptime.alerts.monitorStatus.timerangeOption.weeks', { +export const WEEKS = i18n.translate('xpack.synthetics.alerts.monitorStatus.timerangeOption.weeks', { defaultMessage: 'weeks', }); export const MONTHS_TIME_RANGE = i18n.translate( - 'xpack.uptime.alerts.timerangeUnitSelectable.monthsOption.ariaLabel', + 'xpack.synthetics.alerts.timerangeUnitSelectable.monthsOption.ariaLabel', { defaultMessage: '"Months" time range select item', } ); -export const MONTHS = i18n.translate('xpack.uptime.alerts.monitorStatus.timerangeOption.months', { - defaultMessage: 'months', -}); +export const MONTHS = i18n.translate( + 'xpack.synthetics.alerts.monitorStatus.timerangeOption.months', + { + defaultMessage: 'months', + } +); export const YEARS_TIME_RANGE = i18n.translate( - 'xpack.uptime.alerts.timerangeUnitSelectable.yearsOption.ariaLabel', + 'xpack.synthetics.alerts.timerangeUnitSelectable.yearsOption.ariaLabel', { defaultMessage: '"Years" time range select item', } ); -export const YEARS = i18n.translate('xpack.uptime.alerts.monitorStatus.timerangeOption.years', { +export const YEARS = i18n.translate('xpack.synthetics.alerts.monitorStatus.timerangeOption.years', { defaultMessage: 'years', }); export const ALERT_KUERY_BAR_ARIA = i18n.translate( - 'xpack.uptime.alerts.monitorStatus.filterBar.ariaLabel', + 'xpack.synthetics.alerts.monitorStatus.filterBar.ariaLabel', { defaultMessage: 'Input that allows filtering criteria for the monitor status alert', } ); export const OPEN_THE_POPOVER_DOWN_COUNT = i18n.translate( - 'xpack.uptime.alerts.monitorStatus.numTimesExpression.ariaLabel', + 'xpack.synthetics.alerts.monitorStatus.numTimesExpression.ariaLabel', { defaultMessage: 'Open the popover for down count input', } ); export const ENTER_NUMBER_OF_DOWN_COUNTS = i18n.translate( - 'xpack.uptime.alerts.monitorStatus.numTimesField.ariaLabel', + 'xpack.synthetics.alerts.monitorStatus.numTimesField.ariaLabel', { defaultMessage: 'Enter number of down counts required to trigger the alert', } ); export const MATCHING_MONITORS_DOWN = i18n.translate( - 'xpack.uptime.alerts.monitorStatus.numTimesExpression.matchingMonitors.description', + 'xpack.synthetics.alerts.monitorStatus.numTimesExpression.matchingMonitors.description', { defaultMessage: 'matching monitors are down >', } ); export const ANY_MONITOR_DOWN = i18n.translate( - 'xpack.uptime.alerts.monitorStatus.numTimesExpression.anyMonitors.description', + 'xpack.synthetics.alerts.monitorStatus.numTimesExpression.anyMonitors.description', { defaultMessage: 'any monitor is down >', } ); export const OPEN_THE_POPOVER_TIME_RANGE_VALUE = i18n.translate( - 'xpack.uptime.alerts.monitorStatus.timerangeValueExpression.ariaLabel', + 'xpack.synthetics.alerts.monitorStatus.timerangeValueExpression.ariaLabel', { defaultMessage: 'Open the popover for time range value field', } ); export const ENTER_NUMBER_OF_TIME_UNITS = i18n.translate( - 'xpack.uptime.alerts.monitorStatus.timerangeValueField.ariaLabel', + 'xpack.synthetics.alerts.monitorStatus.timerangeValueField.ariaLabel', { defaultMessage: `Enter the number of time units for the alert's range`, } ); export const ENTER_NUMBER_OF_TIME_UNITS_DESCRIPTION = i18n.translate( - 'xpack.uptime.alerts.monitorStatus.timerangeValueField.expression', + 'xpack.synthetics.alerts.monitorStatus.timerangeValueField.expression', { defaultMessage: 'within', } ); export const ENTER_NUMBER_OF_TIME_UNITS_VALUE = (value: number) => - i18n.translate('xpack.uptime.alerts.monitorStatus.timerangeValueField.value', { + i18n.translate('xpack.synthetics.alerts.monitorStatus.timerangeValueField.value', { defaultMessage: 'last {value}', values: { value }, }); export const ENTER_AVAILABILITY_RANGE_ENABLED = i18n.translate( - 'xpack.uptime.alerts.monitorStatus.availability.isEnabledCheckbox.label', + 'xpack.synthetics.alerts.monitorStatus.availability.isEnabledCheckbox.label', { defaultMessage: 'Availability', } ); export const ENTER_AVAILABILITY_RANGE_POPOVER_ARIA_LABEL = i18n.translate( - 'xpack.uptime.alerts.monitorStatus.availability.timerangeValueField.popover.ariaLabel', + 'xpack.synthetics.alerts.monitorStatus.availability.timerangeValueField.popover.ariaLabel', { defaultMessage: 'Specify availability tracking time range', } ); export const ENTER_AVAILABILITY_RANGE_UNITS_ARIA_LABEL = i18n.translate( - 'xpack.uptime.alerts.monitorStatus.availability.timerangeValueField.ariaLabel', + 'xpack.synthetics.alerts.monitorStatus.availability.timerangeValueField.ariaLabel', { defaultMessage: `Enter the number of units for the alert's availability check.`, } ); export const ENTER_AVAILABILITY_RANGE_UNITS_DESCRIPTION = i18n.translate( - 'xpack.uptime.alerts.monitorStatus.availability.timerangeValueField.expression', + 'xpack.synthetics.alerts.monitorStatus.availability.timerangeValueField.expression', { defaultMessage: 'within the last', } ); export const ENTER_AVAILABILITY_THRESHOLD_ARIA_LABEL = i18n.translate( - 'xpack.uptime.alerts.monitorStatus.availability.threshold.ariaLabel', + 'xpack.synthetics.alerts.monitorStatus.availability.threshold.ariaLabel', { defaultMessage: 'Specify availability thresholds for this alert', } ); export const ENTER_AVAILABILITY_THRESHOLD_INPUT_ARIA_LABEL = i18n.translate( - 'xpack.uptime.alerts.monitorStatus.availability.threshold.input.ariaLabel', + 'xpack.synthetics.alerts.monitorStatus.availability.threshold.input.ariaLabel', { defaultMessage: 'Input an availability threshold to check for this alert', } ); export const ENTER_AVAILABILITY_THRESHOLD_DESCRIPTION = i18n.translate( - 'xpack.uptime.alerts.monitorStatus.availability.threshold.description', + 'xpack.synthetics.alerts.monitorStatus.availability.threshold.description', { defaultMessage: 'matching monitors are up in', description: @@ -198,7 +207,7 @@ export const ENTER_AVAILABILITY_THRESHOLD_DESCRIPTION = i18n.translate( ); export const ENTER_ANY_AVAILABILITY_THRESHOLD_DESCRIPTION = i18n.translate( - 'xpack.uptime.alerts.monitorStatus.availability.threshold.anyMonitorDescription', + 'xpack.synthetics.alerts.monitorStatus.availability.threshold.anyMonitorDescription', { defaultMessage: 'any monitor is up in', description: @@ -207,7 +216,7 @@ export const ENTER_ANY_AVAILABILITY_THRESHOLD_DESCRIPTION = i18n.translate( ); export const ENTER_AVAILABILITY_THRESHOLD_VALUE = (value: string) => - i18n.translate('xpack.uptime.alerts.monitorStatus.availability.threshold.value', { + i18n.translate('xpack.synthetics.alerts.monitorStatus.availability.threshold.value', { defaultMessage: '< {value}% of checks', description: 'This fragment specifies criteria that will cause an alert to fire for uptime monitors', @@ -215,110 +224,122 @@ export const ENTER_AVAILABILITY_THRESHOLD_VALUE = (value: string) => }); export const ENTER_AVAILABILITY_RANGE_SELECT_ARIA = i18n.translate( - 'xpack.uptime.alerts.monitorStatus.availability.unit.selectable', + 'xpack.synthetics.alerts.monitorStatus.availability.unit.selectable', { defaultMessage: 'Use this select to set the availability range units for this alert', } ); export const ENTER_AVAILABILITY_RANGE_SELECT_HEADLINE = i18n.translate( - 'xpack.uptime.alerts.monitorStatus.availability.unit.headline', + 'xpack.synthetics.alerts.monitorStatus.availability.unit.headline', { defaultMessage: 'Select time range unit', } ); -export const ADD_FILTER = i18n.translate('xpack.uptime.alerts.monitorStatus.addFilter', { +export const ADD_FILTER = i18n.translate('xpack.synthetics.alerts.monitorStatus.addFilter', { defaultMessage: `Add filter`, }); -export const LOCATION = i18n.translate('xpack.uptime.alerts.monitorStatus.addFilter.location', { +export const LOCATION = i18n.translate('xpack.synthetics.alerts.monitorStatus.addFilter.location', { defaultMessage: `Location`, }); -export const TAG = i18n.translate('xpack.uptime.alerts.monitorStatus.addFilter.tag', { +export const TAG = i18n.translate('xpack.synthetics.alerts.monitorStatus.addFilter.tag', { defaultMessage: `Tag`, }); -export const PORT = i18n.translate('xpack.uptime.alerts.monitorStatus.addFilter.port', { +export const PORT = i18n.translate('xpack.synthetics.alerts.monitorStatus.addFilter.port', { defaultMessage: `Port`, }); -export const TYPE = i18n.translate('xpack.uptime.alerts.monitorStatus.addFilter.type', { +export const TYPE = i18n.translate('xpack.synthetics.alerts.monitorStatus.addFilter.type', { defaultMessage: `Type`, }); export const TlsTranslations = { - criteriaAriaLabel: i18n.translate('xpack.uptime.alerts.tls.criteriaExpression.ariaLabel', { + criteriaAriaLabel: i18n.translate('xpack.synthetics.alerts.tls.criteriaExpression.ariaLabel', { defaultMessage: 'An expression displaying the criteria for monitor that are watched by this alert', }), - criteriaDescription: i18n.translate('xpack.uptime.alerts.tls.criteriaExpression.description', { - defaultMessage: 'when', - description: - 'The context of this `when` is in the conditional sense, like "when there are three cookies, eat them all".', - }), - criteriaValue: i18n.translate('xpack.uptime.alerts.tls.criteriaExpression.value', { + criteriaDescription: i18n.translate( + 'xpack.synthetics.alerts.tls.criteriaExpression.description', + { + defaultMessage: 'when', + description: + 'The context of this `when` is in the conditional sense, like "when there are three cookies, eat them all".', + } + ), + criteriaValue: i18n.translate('xpack.synthetics.alerts.tls.criteriaExpression.value', { defaultMessage: 'any monitor', }), - expirationAriaLabel: i18n.translate('xpack.uptime.alerts.tls.expirationExpression.ariaLabel', { - defaultMessage: - 'An expression displaying the threshold that will trigger the TLS alert for certificate expiration', - }), + expirationAriaLabel: i18n.translate( + 'xpack.synthetics.alerts.tls.expirationExpression.ariaLabel', + { + defaultMessage: + 'An expression displaying the threshold that will trigger the TLS alert for certificate expiration', + } + ), expirationDescription: i18n.translate( - 'xpack.uptime.alerts.tls.expirationExpression.description', + 'xpack.synthetics.alerts.tls.expirationExpression.description', { defaultMessage: 'has a certificate expiring within', } ), expirationValue: (value?: number) => - i18n.translate('xpack.uptime.alerts.tls.expirationExpression.value', { + i18n.translate('xpack.synthetics.alerts.tls.expirationExpression.value', { defaultMessage: '{value} days', values: { value }, }), - ageAriaLabel: i18n.translate('xpack.uptime.alerts.tls.ageExpression.ariaLabel', { + ageAriaLabel: i18n.translate('xpack.synthetics.alerts.tls.ageExpression.ariaLabel', { defaultMessage: 'An expressing displaying the threshold that will trigger the TLS alert for old certificates', }), - ageDescription: i18n.translate('xpack.uptime.alerts.tls.ageExpression.description', { + ageDescription: i18n.translate('xpack.synthetics.alerts.tls.ageExpression.description', { defaultMessage: 'or older than', }), ageValue: (value?: number) => - i18n.translate('xpack.uptime.alerts.tls.ageExpression.value', { + i18n.translate('xpack.synthetics.alerts.tls.ageExpression.value', { defaultMessage: '{value} days', values: { value }, }), }; export const ToggleFlyoutTranslations = { - toggleButtonAriaLabel: i18n.translate('xpack.uptime.alertsPopover.toggleButton.ariaLabel', { + toggleButtonAriaLabel: i18n.translate('xpack.synthetics.alertsPopover.toggleButton.ariaLabel', { defaultMessage: 'Open alerts and rules context menu', }), - openAlertContextPanelAriaLabel: i18n.translate('xpack.uptime.openAlertContextPanel.ariaLabel', { - defaultMessage: 'Open the rule context panel so you can choose a rule type', - }), - openAlertContextPanelLabel: i18n.translate('xpack.uptime.openAlertContextPanel.label', { + openAlertContextPanelAriaLabel: i18n.translate( + 'xpack.synthetics.openAlertContextPanel.ariaLabel', + { + defaultMessage: 'Open the rule context panel so you can choose a rule type', + } + ), + openAlertContextPanelLabel: i18n.translate('xpack.synthetics.openAlertContextPanel.label', { defaultMessage: 'Create rule', }), - toggleTlsAriaLabel: i18n.translate('xpack.uptime.toggleTlsAlertButton.ariaLabel', { + toggleTlsAriaLabel: i18n.translate('xpack.synthetics.toggleTlsAlertButton.ariaLabel', { defaultMessage: 'Open TLS rule flyout', }), - toggleTlsContent: i18n.translate('xpack.uptime.toggleTlsAlertButton.content', { + toggleTlsContent: i18n.translate('xpack.synthetics.toggleTlsAlertButton.content', { defaultMessage: 'TLS rule', }), - toggleMonitorStatusAriaLabel: i18n.translate('xpack.uptime.toggleAlertFlyout.ariaLabel', { + toggleMonitorStatusAriaLabel: i18n.translate('xpack.synthetics.toggleAlertFlyout.ariaLabel', { defaultMessage: 'Open add rule flyout', }), - toggleMonitorStatusContent: i18n.translate('xpack.uptime.toggleAlertButton.content', { + toggleMonitorStatusContent: i18n.translate('xpack.synthetics.toggleAlertButton.content', { defaultMessage: 'Monitor status rule', }), - navigateToAlertingUIAriaLabel: i18n.translate('xpack.uptime.navigateToAlertingUi', { + navigateToAlertingUIAriaLabel: i18n.translate('xpack.synthetics.navigateToAlertingUi', { defaultMessage: 'Leave Uptime and go to Alerting Management page', }), - navigateToAlertingButtonContent: i18n.translate('xpack.uptime.navigateToAlertingButton.content', { - defaultMessage: 'Manage rules', - }), - toggleAlertFlyoutButtonLabel: i18n.translate('xpack.uptime.alerts.createRulesPanel.title', { + navigateToAlertingButtonContent: i18n.translate( + 'xpack.synthetics.navigateToAlertingButton.content', + { + defaultMessage: 'Manage rules', + } + ), + toggleAlertFlyoutButtonLabel: i18n.translate('xpack.synthetics.alerts.createRulesPanel.title', { defaultMessage: 'Create rules', }), }; diff --git a/x-pack/plugins/synthetics/public/components/overview/empty_state/empty_state_error.tsx b/x-pack/plugins/synthetics/public/components/overview/empty_state/empty_state_error.tsx index bf1459cbcd78..3f2150169e2d 100644 --- a/x-pack/plugins/synthetics/public/components/overview/empty_state/empty_state_error.tsx +++ b/x-pack/plugins/synthetics/public/components/overview/empty_state/empty_state_error.tsx @@ -30,14 +30,14 @@ export const EmptyStateError = ({ errors }: EmptyStateErrorProps) => { {unauthorized ? (

- {i18n.translate('xpack.uptime.emptyStateError.notAuthorized', { + {i18n.translate('xpack.synthetics.emptyStateError.notAuthorized', { defaultMessage: 'You are not authorized to view Uptime data, please contact your system administrator.', })}

) : (

- {i18n.translate('xpack.uptime.emptyStateError.title', { + {i18n.translate('xpack.synthetics.emptyStateError.title', { defaultMessage: 'Error', })}

diff --git a/x-pack/plugins/synthetics/public/components/overview/empty_state/empty_state_loading.tsx b/x-pack/plugins/synthetics/public/components/overview/empty_state/empty_state_loading.tsx index 0943f581f049..0f71c9bafa96 100644 --- a/x-pack/plugins/synthetics/public/components/overview/empty_state/empty_state_loading.tsx +++ b/x-pack/plugins/synthetics/public/components/overview/empty_state/empty_state_loading.tsx @@ -17,7 +17,7 @@ export const EmptyStateLoading = () => (

- {i18n.translate('xpack.uptime.emptyState.loadingMessage', { + {i18n.translate('xpack.synthetics.emptyState.loadingMessage', { defaultMessage: 'Loading…', })}

diff --git a/x-pack/plugins/synthetics/public/components/overview/filter_group/translations.tsx b/x-pack/plugins/synthetics/public/components/overview/filter_group/translations.tsx index 5882631dd8f3..e1cf5e20e14c 100644 --- a/x-pack/plugins/synthetics/public/components/overview/filter_group/translations.tsx +++ b/x-pack/plugins/synthetics/public/components/overview/filter_group/translations.tsx @@ -8,17 +8,17 @@ import { i18n } from '@kbn/i18n'; export const filterLabels = { - LOCATION: i18n.translate('xpack.uptime.filterBar.options.location.name', { + LOCATION: i18n.translate('xpack.synthetics.filterBar.options.location.name', { defaultMessage: 'Location', }), - PORT: i18n.translate('xpack.uptime.filterBar.options.portLabel', { defaultMessage: 'Port' }), + PORT: i18n.translate('xpack.synthetics.filterBar.options.portLabel', { defaultMessage: 'Port' }), - SCHEME: i18n.translate('xpack.uptime.filterBar.options.schemeLabel', { + SCHEME: i18n.translate('xpack.synthetics.filterBar.options.schemeLabel', { defaultMessage: 'Scheme', }), - TAG: i18n.translate('xpack.uptime.filterBar.options.tagsLabel', { + TAG: i18n.translate('xpack.synthetics.filterBar.options.tagsLabel', { defaultMessage: 'Tag', }), }; diff --git a/x-pack/plugins/synthetics/public/components/overview/monitor_list/__snapshots__/status_filter.test.tsx.snap b/x-pack/plugins/synthetics/public/components/overview/monitor_list/__snapshots__/status_filter.test.tsx.snap index ed344510c1cd..98a60c79ca49 100644 --- a/x-pack/plugins/synthetics/public/components/overview/monitor_list/__snapshots__/status_filter.test.tsx.snap +++ b/x-pack/plugins/synthetics/public/components/overview/monitor_list/__snapshots__/status_filter.test.tsx.snap @@ -6,7 +6,7 @@ exports[`StatusFilterComponent renders without errors for valid props 1`] = ` >