diff --git a/.ci/packer_cache.sh b/.ci/packer_cache.sh index 11f9ccaeddb1e9..e4b5e35e1e4a9a 100755 --- a/.ci/packer_cache.sh +++ b/.ci/packer_cache.sh @@ -2,59 +2,5 @@ set -e -branch="$(git rev-parse --abbrev-ref HEAD 2> /dev/null)" - -# run setup script that gives us node, yarn, and bootstraps the project -source src/dev/ci_setup/setup.sh; - -# download es snapshots -node scripts/es snapshot --download-only; -node scripts/es snapshot --license=oss --download-only; - -# download reporting browsers -(cd "x-pack" && yarn gulp prepare); - -# cache the chromedriver archive -chromedriverDistVersion="$(node -e "console.log(require('chromedriver').version)")" -chromedriverPkgVersion="$(node -e "console.log(require('./package.json').devDependencies.chromedriver)")" -if [ -z "$chromedriverDistVersion" ] || [ -z "$chromedriverPkgVersion" ]; then - echo "UNABLE TO DETERMINE CHROMEDRIVER VERSIONS" - exit 1 -fi -mkdir -p .chromedriver -curl "https://chromedriver.storage.googleapis.com/$chromedriverDistVersion/chromedriver_linux64.zip" > .chromedriver/chromedriver.zip -echo "$chromedriverPkgVersion" > .chromedriver/pkgVersion - -# cache the geckodriver archive -geckodriverPkgVersion="$(node -e "console.log(require('./package.json').devDependencies.geckodriver)")" -if [ -z "$geckodriverPkgVersion" ]; then - echo "UNABLE TO DETERMINE geckodriver VERSIONS" - exit 1 -fi -mkdir -p ".geckodriver" -cp "node_modules/geckodriver/geckodriver.tar.gz" .geckodriver/geckodriver.tar.gz -echo "$geckodriverPkgVersion" > .geckodriver/pkgVersion - -echo "Creating bootstrap_cache archive" - -# archive cacheable directories -mkdir -p "$HOME/.kibana/bootstrap_cache" -tar -cf "$HOME/.kibana/bootstrap_cache/$branch.tar" \ - x-pack/plugins/reporting/.chromium \ - .es \ - .chromedriver \ - .geckodriver; - -echo "Adding node_modules" -# Find all of the node_modules directories that aren't test fixtures, and aren't inside other node_modules directories, and append them to the tar -find . -type d -name node_modules -not -path '*__fixtures__*' -prune -print0 | xargs -0I % tar -rf "$HOME/.kibana/bootstrap_cache/$branch.tar" "%" - -echo "created $HOME/.kibana/bootstrap_cache/$branch.tar" - -if [ "$branch" == "master" ]; then - echo "Creating bootstrap cache for 7.x"; - - git clone https://github.com/elastic/kibana.git --branch 7.x --depth 1 /tmp/kibana-7.x - (cd /tmp/kibana-7.x && ./.ci/packer_cache.sh); - rm -rf /tmp/kibana-7.x; -fi +./.ci/packer_cache_for_branch.sh master +./.ci/packer_cache_for_branch.sh 7.x diff --git a/.ci/packer_cache_for_branch.sh b/.ci/packer_cache_for_branch.sh new file mode 100755 index 00000000000000..a9fbe781915b68 --- /dev/null +++ b/.ci/packer_cache_for_branch.sh @@ -0,0 +1,62 @@ +#!/usr/bin/env bash + +set -e + +branch="$1" +checkoutDir="$(pwd)" + +if [[ "$branch" != "master" ]]; then + checkoutDir="/tmp/kibana-$branch" + git clone https://github.com/elastic/kibana.git --branch "$branch" --depth 1 "$checkoutDir" + cd "$checkoutDir" +fi + +source src/dev/ci_setup/setup.sh; + +# download es snapshots +node scripts/es snapshot --download-only; +node scripts/es snapshot --license=oss --download-only; + +# download reporting browsers +(cd "x-pack" && yarn gulp prepare); + +# cache the chromedriver archive +chromedriverDistVersion="$(node -e "console.log(require('chromedriver').version)")" +chromedriverPkgVersion="$(node -e "console.log(require('./package.json').devDependencies.chromedriver)")" +if [ -z "$chromedriverDistVersion" ] || [ -z "$chromedriverPkgVersion" ]; then + echo "UNABLE TO DETERMINE CHROMEDRIVER VERSIONS" + exit 1 +fi +mkdir -p .chromedriver +curl "https://chromedriver.storage.googleapis.com/$chromedriverDistVersion/chromedriver_linux64.zip" > .chromedriver/chromedriver.zip +echo "$chromedriverPkgVersion" > .chromedriver/pkgVersion + +# cache the geckodriver archive +geckodriverPkgVersion="$(node -e "console.log(require('./package.json').devDependencies.geckodriver)")" +if [ -z "$geckodriverPkgVersion" ]; then + echo "UNABLE TO DETERMINE geckodriver VERSIONS" + exit 1 +fi +mkdir -p ".geckodriver" +cp "node_modules/geckodriver/geckodriver.tar.gz" .geckodriver/geckodriver.tar.gz +echo "$geckodriverPkgVersion" > .geckodriver/pkgVersion + +echo "Creating bootstrap_cache archive" + +# archive cacheable directories +mkdir -p "$HOME/.kibana/bootstrap_cache" +tar -cf "$HOME/.kibana/bootstrap_cache/$branch.tar" \ + x-pack/plugins/reporting/.chromium \ + .es \ + .chromedriver \ + .geckodriver; + +echo "Adding node_modules" +# Find all of the node_modules directories that aren't test fixtures, and aren't inside other node_modules directories, and append them to the tar +find . -type d -name node_modules -not -path '*__fixtures__*' -prune -print0 | xargs -0I % tar -rf "$HOME/.kibana/bootstrap_cache/$branch.tar" "%" + +echo "created $HOME/.kibana/bootstrap_cache/$branch.tar" + +if [[ "$branch" != "master" ]]; then + rm --preserve-root -rf "$checkoutDir" +fi diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.irequesttypesmap.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.irequesttypesmap.md index a9bb8f1eb9d6df..3f5e4ba0f77999 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.irequesttypesmap.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.irequesttypesmap.md @@ -4,6 +4,8 @@ ## IRequestTypesMap interface +The map of search strategy IDs to the corresponding request type definitions. + Signature: ```typescript diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iresponsetypesmap.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iresponsetypesmap.md index fe5fa0a5d3a331..629ab4347eda80 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iresponsetypesmap.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iresponsetypesmap.md @@ -4,6 +4,8 @@ ## IResponseTypesMap interface +The map of search strategy IDs to the corresponding response type definitions. + Signature: ```typescript diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearch.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearch.md index 6e037f5161b532..96991579c17169 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearch.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearch.md @@ -7,5 +7,5 @@ Signature: ```typescript -export declare type ISearch = (request: IRequestTypesMap[T], options?: ISearchOptions) => Promise; +export declare type ISearch = (context: RequestHandlerContext, request: IRequestTypesMap[T], options?: ISearchOptions) => Promise; ``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchcancel.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchcancel.md index 99c30515e8da6a..b5a687d1b19d84 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchcancel.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchcancel.md @@ -7,5 +7,5 @@ Signature: ```typescript -export declare type ISearchCancel = (id: string) => Promise; +export declare type ISearchCancel = (context: RequestHandlerContext, id: string) => Promise; ``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchcontext.config_.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchcontext.config_.md deleted file mode 100644 index 364d44dba758af..00000000000000 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchcontext.config_.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [ISearchContext](./kibana-plugin-plugins-data-server.isearchcontext.md) > [config$](./kibana-plugin-plugins-data-server.isearchcontext.config_.md) - -## ISearchContext.config$ property - -Signature: - -```typescript -config$: Observable; -``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchcontext.core.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchcontext.core.md deleted file mode 100644 index 9d571c25d94bd5..00000000000000 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchcontext.core.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [ISearchContext](./kibana-plugin-plugins-data-server.isearchcontext.md) > [core](./kibana-plugin-plugins-data-server.isearchcontext.core.md) - -## ISearchContext.core property - -Signature: - -```typescript -core: CoreSetup; -``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchcontext.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchcontext.md deleted file mode 100644 index 1c3c5ec78f894e..00000000000000 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchcontext.md +++ /dev/null @@ -1,19 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [ISearchContext](./kibana-plugin-plugins-data-server.isearchcontext.md) - -## ISearchContext interface - -Signature: - -```typescript -export interface ISearchContext -``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [config$](./kibana-plugin-plugins-data-server.isearchcontext.config_.md) | Observable<SharedGlobalConfig> | | -| [core](./kibana-plugin-plugins-data-server.isearchcontext.core.md) | CoreSetup | | - diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchoptions.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchoptions.md index 0319048f4418bc..49412fc42d3b5f 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchoptions.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchoptions.md @@ -14,5 +14,5 @@ export interface ISearchOptions | Property | Type | Description | | --- | --- | --- | -| [signal](./kibana-plugin-plugins-data-server.isearchoptions.signal.md) | AbortSignal | | +| [signal](./kibana-plugin-plugins-data-server.isearchoptions.signal.md) | AbortSignal | An AbortSignal that allows the caller of search to abort a search request. | diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchoptions.signal.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchoptions.signal.md index 7da5c182b2e0f7..948dfd66da7a03 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchoptions.signal.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchoptions.signal.md @@ -4,6 +4,8 @@ ## ISearchOptions.signal property +An `AbortSignal` that allows the caller of `search` to abort a search request. + Signature: ```typescript diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchsetup.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchsetup.md new file mode 100644 index 00000000000000..93e253b2e98a3f --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchsetup.md @@ -0,0 +1,18 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [ISearchSetup](./kibana-plugin-plugins-data-server.isearchsetup.md) + +## ISearchSetup interface + +Signature: + +```typescript +export interface ISearchSetup +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [registerSearchStrategy](./kibana-plugin-plugins-data-server.isearchsetup.registersearchstrategy.md) | TRegisterSearchStrategy | Extension point exposed for other plugins to register their own search strategies. | + diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchsetup.registersearchstrategy.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchsetup.registersearchstrategy.md new file mode 100644 index 00000000000000..c06b8b00806bfe --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchsetup.registersearchstrategy.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [ISearchSetup](./kibana-plugin-plugins-data-server.isearchsetup.md) > [registerSearchStrategy](./kibana-plugin-plugins-data-server.isearchsetup.registersearchstrategy.md) + +## ISearchSetup.registerSearchStrategy property + +Extension point exposed for other plugins to register their own search strategies. + +Signature: + +```typescript +registerSearchStrategy: TRegisterSearchStrategy; +``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstart.getsearchstrategy.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstart.getsearchstrategy.md new file mode 100644 index 00000000000000..0ba4bf578d6cc9 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstart.getsearchstrategy.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [ISearchStart](./kibana-plugin-plugins-data-server.isearchstart.md) > [getSearchStrategy](./kibana-plugin-plugins-data-server.isearchstart.getsearchstrategy.md) + +## ISearchStart.getSearchStrategy property + +Get other registered search strategies. For example, if a new strategy needs to use the already-registered ES search strategy, it can use this function to accomplish that. + +Signature: + +```typescript +getSearchStrategy: TGetSearchStrategy; +``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstart.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstart.md new file mode 100644 index 00000000000000..abe72396f61e18 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstart.md @@ -0,0 +1,18 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [ISearchStart](./kibana-plugin-plugins-data-server.isearchstart.md) + +## ISearchStart interface + +Signature: + +```typescript +export interface ISearchStart +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [getSearchStrategy](./kibana-plugin-plugins-data-server.isearchstart.getsearchstrategy.md) | TGetSearchStrategy | Get other registered search strategies. For example, if a new strategy needs to use the already-registered ES search strategy, it can use this function to accomplish that. | + diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstrategy.cancel.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstrategy.cancel.md new file mode 100644 index 00000000000000..c1e0c3d9f23300 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstrategy.cancel.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [ISearchStrategy](./kibana-plugin-plugins-data-server.isearchstrategy.md) > [cancel](./kibana-plugin-plugins-data-server.isearchstrategy.cancel.md) + +## ISearchStrategy.cancel property + +Signature: + +```typescript +cancel?: ISearchCancel; +``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstrategy.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstrategy.md new file mode 100644 index 00000000000000..167c6ab6e5a16f --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstrategy.md @@ -0,0 +1,21 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [ISearchStrategy](./kibana-plugin-plugins-data-server.isearchstrategy.md) + +## ISearchStrategy interface + +Search strategy interface contains a search method that takes in a request and returns a promise that resolves to a response. + +Signature: + +```typescript +export interface ISearchStrategy +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [cancel](./kibana-plugin-plugins-data-server.isearchstrategy.cancel.md) | ISearchCancel<T> | | +| [search](./kibana-plugin-plugins-data-server.isearchstrategy.search.md) | ISearch<T> | | + diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstrategy.search.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstrategy.search.md new file mode 100644 index 00000000000000..34a17ca87807a4 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstrategy.search.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [ISearchStrategy](./kibana-plugin-plugins-data-server.isearchstrategy.md) > [search](./kibana-plugin-plugins-data-server.isearchstrategy.search.md) + +## ISearchStrategy.search property + +Signature: + +```typescript +search: ISearch; +``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.md index 0efbe8ed4ed643..f492ba2843a697 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.md @@ -39,10 +39,12 @@ | [IIndexPattern](./kibana-plugin-plugins-data-server.iindexpattern.md) | | | [IndexPatternAttributes](./kibana-plugin-plugins-data-server.indexpatternattributes.md) | Use data plugin interface instead | | [IndexPatternFieldDescriptor](./kibana-plugin-plugins-data-server.indexpatternfielddescriptor.md) | | -| [IRequestTypesMap](./kibana-plugin-plugins-data-server.irequesttypesmap.md) | | -| [IResponseTypesMap](./kibana-plugin-plugins-data-server.iresponsetypesmap.md) | | -| [ISearchContext](./kibana-plugin-plugins-data-server.isearchcontext.md) | | +| [IRequestTypesMap](./kibana-plugin-plugins-data-server.irequesttypesmap.md) | The map of search strategy IDs to the corresponding request type definitions. | +| [IResponseTypesMap](./kibana-plugin-plugins-data-server.iresponsetypesmap.md) | The map of search strategy IDs to the corresponding response type definitions. | | [ISearchOptions](./kibana-plugin-plugins-data-server.isearchoptions.md) | | +| [ISearchSetup](./kibana-plugin-plugins-data-server.isearchsetup.md) | | +| [ISearchStart](./kibana-plugin-plugins-data-server.isearchstart.md) | | +| [ISearchStrategy](./kibana-plugin-plugins-data-server.isearchstrategy.md) | Search strategy interface contains a search method that takes in a request and returns a promise that resolves to a response. | | [KueryNode](./kibana-plugin-plugins-data-server.kuerynode.md) | | | [PluginSetup](./kibana-plugin-plugins-data-server.pluginsetup.md) | | | [PluginStart](./kibana-plugin-plugins-data-server.pluginstart.md) | | @@ -73,5 +75,5 @@ | [ISearch](./kibana-plugin-plugins-data-server.isearch.md) | | | [ISearchCancel](./kibana-plugin-plugins-data-server.isearchcancel.md) | | | [ParsedInterval](./kibana-plugin-plugins-data-server.parsedinterval.md) | | -| [TSearchStrategyProvider](./kibana-plugin-plugins-data-server.tsearchstrategyprovider.md) | Search strategy provider creates an instance of a search strategy with the request handler context bound to it. This way every search strategy can use whatever information they require from the request context. | +| [TStrategyTypes](./kibana-plugin-plugins-data-server.tstrategytypes.md) | Contains all known strategy type identifiers that will be used to map to request and response shapes. Plugins that wish to add their own custom search strategies should extend this type via:const MY\_STRATEGY = 'MY\_STRATEGY';declare module 'src/plugins/search/server' { export interface IRequestTypesMap { \[MY\_STRATEGY\]: IMySearchRequest; }export interface IResponseTypesMap { \[MY\_STRATEGY\]: IMySearchResponse } } | diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.plugin.setup.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.plugin.setup.md index bd617990a00a2f..13c69d6bf7548e 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.plugin.setup.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.plugin.setup.md @@ -7,11 +7,11 @@ Signature: ```typescript -setup(core: CoreSetup, { usageCollection }: DataPluginSetupDependencies): { +setup(core: CoreSetup, { usageCollection }: DataPluginSetupDependencies): { + search: ISearchSetup; fieldFormats: { register: (customFieldFormat: import("../public").FieldFormatInstanceType) => number; }; - search: ISearchSetup; }; ``` @@ -19,15 +19,15 @@ setup(core: CoreSetup, { usageCollection }: DataPluginSetupDependencies): { | Parameter | Type | Description | | --- | --- | --- | -| core | CoreSetup | | +| core | CoreSetup<object, DataPluginStart> | | | { usageCollection } | DataPluginSetupDependencies | | Returns: `{ + search: ISearchSetup; fieldFormats: { register: (customFieldFormat: import("../public").FieldFormatInstanceType) => number; }; - search: ISearchSetup; }` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.plugin.start.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.plugin.start.md index 2a30cd3e68158c..2c7a833ab641bc 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.plugin.start.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.plugin.start.md @@ -8,8 +8,9 @@ ```typescript start(core: CoreStart): { + search: ISearchStart; fieldFormats: { - fieldFormatServiceFactory: (uiSettings: import("kibana/server").IUiSettingsClient) => Promise; + fieldFormatServiceFactory: (uiSettings: import("../../../core/server").IUiSettingsClient) => Promise; }; }; ``` @@ -23,8 +24,9 @@ start(core: CoreStart): { Returns: `{ + search: ISearchStart; fieldFormats: { - fieldFormatServiceFactory: (uiSettings: import("kibana/server").IUiSettingsClient) => Promise; + fieldFormatServiceFactory: (uiSettings: import("../../../core/server").IUiSettingsClient) => Promise; }; }` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.pluginstart.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.pluginstart.md index b7d6a7e8a83fd2..1377d82123d412 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.pluginstart.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.pluginstart.md @@ -15,4 +15,5 @@ export interface DataPluginStart | Property | Type | Description | | --- | --- | --- | | [fieldFormats](./kibana-plugin-plugins-data-server.pluginstart.fieldformats.md) | FieldFormatsStart | | +| [search](./kibana-plugin-plugins-data-server.pluginstart.search.md) | ISearchStart | | diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.pluginstart.search.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.pluginstart.search.md new file mode 100644 index 00000000000000..3144d8c40b780e --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.pluginstart.search.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [PluginStart](./kibana-plugin-plugins-data-server.pluginstart.md) > [search](./kibana-plugin-plugins-data-server.pluginstart.search.md) + +## PluginStart.search property + +Signature: + +```typescript +search: ISearchStart; +``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.tsearchstrategyprovider.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.tsearchstrategyprovider.md deleted file mode 100644 index f528f48a68f727..00000000000000 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.tsearchstrategyprovider.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [TSearchStrategyProvider](./kibana-plugin-plugins-data-server.tsearchstrategyprovider.md) - -## TSearchStrategyProvider type - -Search strategy provider creates an instance of a search strategy with the request handler context bound to it. This way every search strategy can use whatever information they require from the request context. - -Signature: - -```typescript -export declare type TSearchStrategyProvider = (context: ISearchContext, caller: APICaller, search: ISearchGeneric) => ISearchStrategy; -``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.tstrategytypes.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.tstrategytypes.md new file mode 100644 index 00000000000000..443d8d1b424d0b --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.tstrategytypes.md @@ -0,0 +1,19 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [TStrategyTypes](./kibana-plugin-plugins-data-server.tstrategytypes.md) + +## TStrategyTypes type + +Contains all known strategy type identifiers that will be used to map to request and response shapes. Plugins that wish to add their own custom search strategies should extend this type via: + +const MY\_STRATEGY = 'MY\_STRATEGY'; + +declare module 'src/plugins/search/server' { export interface IRequestTypesMap { \[MY\_STRATEGY\]: IMySearchRequest; } + +export interface IResponseTypesMap { \[MY\_STRATEGY\]: IMySearchResponse } } + +Signature: + +```typescript +export declare type TStrategyTypes = typeof ES_SEARCH_STRATEGY | string; +``` diff --git a/examples/demo_search/server/async_demo_search_strategy.ts b/examples/demo_search/server/async_demo_search_strategy.ts index 7ed5062acba484..2eda0f4d09e11b 100644 --- a/examples/demo_search/server/async_demo_search_strategy.ts +++ b/examples/demo_search/server/async_demo_search_strategy.ts @@ -17,30 +17,32 @@ * under the License. */ -import { TSearchStrategyProvider } from '../../../src/plugins/data/server'; -import { ASYNC_DEMO_SEARCH_STRATEGY } from '../common'; - -function getFibonacciSequence(n = 0) { - const beginning = [0, 1].slice(0, n); - return Array(Math.max(0, n)) - .fill(null) - .reduce((sequence, value, i) => { - if (i < 2) return sequence; - return [...sequence, sequence[i - 1] + sequence[i - 2]]; - }, beginning); -} - -const generateId = (() => { - let id = 0; - return () => `${id++}`; -})(); - -const loadedMap = new Map(); -const totalMap = new Map(); - -export const asyncDemoSearchStrategyProvider: TSearchStrategyProvider = () => { +import { ISearchStrategy } from '../../../src/plugins/data/server'; +import { ASYNC_DEMO_SEARCH_STRATEGY, IAsyncDemoRequest } from '../common'; + +export const asyncDemoSearchStrategyProvider = (): ISearchStrategy< + typeof ASYNC_DEMO_SEARCH_STRATEGY +> => { + function getFibonacciSequence(n = 0) { + const beginning = [0, 1].slice(0, n); + return Array(Math.max(0, n)) + .fill(null) + .reduce((sequence, value, i) => { + if (i < 2) return sequence; + return [...sequence, sequence[i - 1] + sequence[i - 2]]; + }, beginning); + } + + const generateId = (() => { + let id = 0; + return () => `${id++}`; + })(); + + const loadedMap = new Map(); + const totalMap = new Map(); + return { - search: async (request) => { + search: async (context, request: IAsyncDemoRequest) => { const id = request.id ?? generateId(); const loaded = (loadedMap.get(id) ?? 0) + 1; @@ -52,7 +54,7 @@ export const asyncDemoSearchStrategyProvider: TSearchStrategyProvider { + cancel: async (context, id) => { loadedMap.delete(id); totalMap.delete(id); }, diff --git a/examples/demo_search/server/demo_search_strategy.ts b/examples/demo_search/server/demo_search_strategy.ts index a1fd0e45dbc8e7..36280ad22e83ca 100644 --- a/examples/demo_search/server/demo_search_strategy.ts +++ b/examples/demo_search/server/demo_search_strategy.ts @@ -17,12 +17,12 @@ * under the License. */ -import { TSearchStrategyProvider } from '../../../src/plugins/data/server'; -import { DEMO_SEARCH_STRATEGY } from '../common'; +import { ISearchStrategy } from '../../../src/plugins/data/server'; +import { DEMO_SEARCH_STRATEGY, IDemoRequest } from '../common'; -export const demoSearchStrategyProvider: TSearchStrategyProvider = () => { +export const demoSearchStrategyProvider = (): ISearchStrategy => { return { - search: (request) => { + search: (context, request: IDemoRequest) => { return Promise.resolve({ greeting: request.mood === 'happy' diff --git a/examples/demo_search/server/index.ts b/examples/demo_search/server/index.ts index 6289b684b2b1e7..368575b705c90a 100644 --- a/examples/demo_search/server/index.ts +++ b/examples/demo_search/server/index.ts @@ -17,9 +17,6 @@ * under the License. */ -import { PluginInitializerContext, PluginInitializer } from 'kibana/server'; import { DemoDataPlugin } from './plugin'; -export const plugin: PluginInitializer = ( - initializerContext: PluginInitializerContext -) => new DemoDataPlugin(initializerContext); +export const plugin = () => new DemoDataPlugin(); diff --git a/examples/demo_search/server/plugin.ts b/examples/demo_search/server/plugin.ts index 49fbae43e3aa28..8a72b5007f988c 100644 --- a/examples/demo_search/server/plugin.ts +++ b/examples/demo_search/server/plugin.ts @@ -17,7 +17,7 @@ * under the License. */ -import { Plugin, CoreSetup, PluginInitializerContext } from 'kibana/server'; +import { Plugin, CoreSetup } from 'kibana/server'; import { PluginSetup as DataPluginSetup } from 'src/plugins/data/server'; import { demoSearchStrategyProvider } from './demo_search_strategy'; import { @@ -56,18 +56,13 @@ declare module '../../../src/plugins/data/server' { } export class DemoDataPlugin implements Plugin { - constructor(private initializerContext: PluginInitializerContext) {} + constructor() {} public setup(core: CoreSetup, deps: IDemoSearchExplorerDeps) { - deps.data.search.registerSearchStrategyProvider( - this.initializerContext.opaqueId, - DEMO_SEARCH_STRATEGY, - demoSearchStrategyProvider - ); - deps.data.search.registerSearchStrategyProvider( - this.initializerContext.opaqueId, + deps.data.search.registerSearchStrategy(DEMO_SEARCH_STRATEGY, demoSearchStrategyProvider()); + deps.data.search.registerSearchStrategy( ASYNC_DEMO_SEARCH_STRATEGY, - asyncDemoSearchStrategyProvider + asyncDemoSearchStrategyProvider() ); } diff --git a/examples/embeddable_examples/kibana.json b/examples/embeddable_examples/kibana.json index b3ee0de096989e..486c6322fad933 100644 --- a/examples/embeddable_examples/kibana.json +++ b/examples/embeddable_examples/kibana.json @@ -6,5 +6,5 @@ "ui": true, "requiredPlugins": ["embeddable"], "optionalPlugins": [], - "extraPublicDirs": ["public/todo", "public/hello_world"] + "extraPublicDirs": ["public/todo", "public/hello_world", "public/todo/todo_ref_embeddable"] } diff --git a/examples/search_explorer/public/search_api.tsx b/examples/search_explorer/public/search_api.tsx index c77ec725c6890d..4a0e9afd20c85d 100644 --- a/examples/search_explorer/public/search_api.tsx +++ b/examples/search_explorer/public/search_api.tsx @@ -23,11 +23,6 @@ import { GuideSection } from './guide_section'; import publicSearch from '!!raw-loader!./../../../src/plugins/data/public/search/i_search'; // @ts-ignore import publicPlugin from '!!raw-loader!./../../../src/plugins/data/public/search/search_service'; - -// @ts-ignore -import serverSetupContract from '!!raw-loader!./../../../src/plugins/data/server/search/i_search_setup'; -// @ts-ignore -import serverSearch from '!!raw-loader!./../../../src/plugins/data/server/search/i_search'; // @ts-ignore import serverPlugin from '!!raw-loader!./../../../src/plugins/data/server/search/search_service'; @@ -54,14 +49,6 @@ export const SearchApiPage = () => ( description: 'search_service.ts', snippet: serverPlugin, }, - { - description: `i_search_setup.ts`, - snippet: serverSetupContract, - }, - { - description: 'i_search', - snippet: serverSearch, - }, ], }, ]} diff --git a/packages/kbn-optimizer/src/optimizer/observe_worker.ts b/packages/kbn-optimizer/src/optimizer/observe_worker.ts index 4527052fa821a4..fef3efc13a516a 100644 --- a/packages/kbn-optimizer/src/optimizer/observe_worker.ts +++ b/packages/kbn-optimizer/src/optimizer/observe_worker.ts @@ -22,7 +22,7 @@ import { inspect } from 'util'; import execa from 'execa'; import * as Rx from 'rxjs'; -import { map, takeUntil } from 'rxjs/operators'; +import { map, takeUntil, first, ignoreElements } from 'rxjs/operators'; import { isWorkerMsg, WorkerConfig, WorkerMsg, Bundle, BundleRefs } from '../common'; @@ -68,19 +68,11 @@ if (inspectFlagIndex !== -1) { function usingWorkerProc( config: OptimizerConfig, - workerConfig: WorkerConfig, - bundles: Bundle[], fn: (proc: execa.ExecaChildProcess) => Rx.Observable ) { return Rx.using( (): ProcResource => { - const args = [ - JSON.stringify(workerConfig), - JSON.stringify(bundles.map((b) => b.toSpec())), - BundleRefs.fromBundles(config.bundles).toSpecJson(), - ]; - - const proc = execa.node(require.resolve('../worker/run_worker'), args, { + const proc = execa.node(require.resolve('../worker/run_worker'), [], { nodeOptions: [ ...(inspectFlag && config.inspectWorkers ? [`${inspectFlag}=${inspectPortCounter++}`] @@ -129,6 +121,51 @@ function observeStdio$(stream: Readable, name: WorkerStdio['stream']) { ); } +/** + * We used to pass configuration to the worker as JSON encoded arguments, but they + * grew too large for argv, especially on Windows, so we had to move to an async init + * where we send the args over IPC. To keep the logic simple we basically mock the + * argv behavior and don't use complicated messages or anything so that state can + * be initialized in the worker before most of the code is run. + */ +function initWorker( + proc: execa.ExecaChildProcess, + config: OptimizerConfig, + workerConfig: WorkerConfig, + bundles: Bundle[] +) { + const msg$ = Rx.fromEvent<[unknown]>(proc, 'message').pipe( + // validate the initialization messages from the process + map(([msg]) => { + if (typeof msg === 'string') { + switch (msg) { + case 'init': + return 'init' as const; + case 'ready': + return 'ready' as const; + } + } + + throw new Error(`unexpected message from worker while initializing: [${inspect(msg)}]`); + }) + ); + + return Rx.concat( + msg$.pipe(first((msg) => msg === 'init')), + Rx.defer(() => { + proc.send({ + args: [ + JSON.stringify(workerConfig), + JSON.stringify(bundles.map((b) => b.toSpec())), + BundleRefs.fromBundles(config.bundles).toSpecJson(), + ], + }); + return []; + }), + msg$.pipe(first((msg) => msg === 'ready')) + ).pipe(ignoreElements()); +} + /** * Start a worker process with the specified `workerConfig` and * `bundles` and return an observable of the events related to @@ -140,10 +177,11 @@ export function observeWorker( workerConfig: WorkerConfig, bundles: Bundle[] ): Rx.Observable { - return usingWorkerProc(config, workerConfig, bundles, (proc) => { - let lastMsg: WorkerMsg; + return usingWorkerProc(config, (proc) => { + const init$ = initWorker(proc, config, workerConfig, bundles); - return Rx.merge( + let lastMsg: WorkerMsg; + const worker$: Rx.Observable = Rx.merge( Rx.of({ type: 'worker started', bundles, @@ -201,5 +239,7 @@ export function observeWorker( ) ) ); + + return Rx.concat(init$, worker$); }); } diff --git a/packages/kbn-optimizer/src/worker/bundle_refs_plugin.ts b/packages/kbn-optimizer/src/worker/bundle_refs_plugin.ts index 6defcaa787b7d7..9c4d5ed7f8a983 100644 --- a/packages/kbn-optimizer/src/worker/bundle_refs_plugin.ts +++ b/packages/kbn-optimizer/src/worker/bundle_refs_plugin.ts @@ -40,51 +40,69 @@ interface RequestData { type Callback = (error?: any, result?: T) => void; type ModuleFactory = (data: RequestData, callback: Callback) => void; -/** - * Isolate the weired type juggling we have to do to add a hook to the webpack compiler - */ -function hookIntoCompiler( - compiler: webpack.Compiler, - handler: (context: string, request: string) => Promise -) { - compiler.hooks.compile.tap('BundleRefsPlugin', (compilationParams: any) => { - compilationParams.normalModuleFactory.hooks.factory.tap( - 'BundleRefsPlugin/normalModuleFactory/factory', - (wrappedFactory: ModuleFactory): ModuleFactory => (data, callback) => { - const context = data.context; - const dep = data.dependencies[0]; - - handler(context, dep.request).then( - (result) => { - if (!result) { - wrappedFactory(data, callback); - } else { - callback(undefined, result); - } - }, - (error) => callback(error) - ); - } - ); - }); -} - export class BundleRefsPlugin { - private resolvedRequestCache = new Map>(); + private readonly resolvedRefEntryCache = new Map>(); + private readonly resolvedRequestCache = new Map>(); + private readonly ignorePrefix = Path.resolve(this.bundle.contextDir) + Path.sep; - constructor(private readonly bundle: Bundle, public readonly bundleRefs: BundleRefs) {} + constructor(private readonly bundle: Bundle, private readonly bundleRefs: BundleRefs) {} - apply(compiler: webpack.Compiler) { - hookIntoCompiler(compiler, async (context, request) => { - const ref = await this.resolveRef(context, request); - if (ref) { - return new BundleRefModule(ref.exportId); + /** + * Called by webpack when the plugin is passed in the webpack config + */ + public apply(compiler: webpack.Compiler) { + // called whenever the compiler starts to compile, passed the params + // that will be used to create the compilation + compiler.hooks.compile.tap('BundleRefsPlugin', (compilationParams: any) => { + // clear caches because a new compilation is starting, meaning that files have + // changed and we should re-run resolutions + this.resolvedRefEntryCache.clear(); + this.resolvedRequestCache.clear(); + + // hook into the creation of NormalModule instances in webpack, if the import + // statement leading to the creation of the module is pointing to a bundleRef + // entry then create a BundleRefModule instead of a NormalModule. + compilationParams.normalModuleFactory.hooks.factory.tap( + 'BundleRefsPlugin/normalModuleFactory/factory', + (wrappedFactory: ModuleFactory): ModuleFactory => (data, callback) => { + const context = data.context; + const dep = data.dependencies[0]; + + this.maybeReplaceImport(context, dep.request).then( + (module) => { + if (!module) { + wrappedFactory(data, callback); + } else { + callback(undefined, module); + } + }, + (error) => callback(error) + ); + } + ); + }); + } + + private cachedResolveRefEntry(ref: BundleRef) { + const cached = this.resolvedRefEntryCache.get(ref); + + if (cached) { + return cached; + } + + const absoluteRequest = Path.resolve(ref.contextDir, ref.entry); + const promise = this.cachedResolveRequest(absoluteRequest).then((resolved) => { + if (!resolved) { + throw new Error(`Unable to resolve request [${ref.entry}] relative to [${ref.contextDir}]`); } + + return resolved; }); + this.resolvedRefEntryCache.set(ref, promise); + return promise; } - private cachedResolveRequest(context: string, request: string) { - const absoluteRequest = Path.resolve(context, request); + private cachedResolveRequest(absoluteRequest: string) { const cached = this.resolvedRequestCache.get(absoluteRequest); if (cached) { @@ -102,6 +120,7 @@ export class BundleRefsPlugin { return absoluteRequest; } + // look for an index file in directories if (stats?.isDirectory()) { for (const ext of RESOLVE_EXTENSIONS) { const indexPath = Path.resolve(absoluteRequest, `index${ext}`); @@ -112,6 +131,15 @@ export class BundleRefsPlugin { } } + // look for a file with one of the supported extensions + for (const ext of RESOLVE_EXTENSIONS) { + const filePath = `${absoluteRequest}${ext}`; + const fileStats = await safeStat(filePath); + if (fileStats?.isFile()) { + return filePath; + } + } + return; } @@ -121,7 +149,7 @@ export class BundleRefsPlugin { * then an error is thrown. If the request does not resolve to a bundleRef then * undefined is returned. Otherwise it returns the referenced bundleRef. */ - private async resolveRef(context: string, request: string) { + private async maybeReplaceImport(context: string, request: string) { // ignore imports that have loaders defined or are not relative seeming if (request.includes('!') || !request.startsWith('.')) { return; @@ -132,7 +160,12 @@ export class BundleRefsPlugin { return; } - const resolved = await this.cachedResolveRequest(context, request); + const absoluteRequest = Path.resolve(context, request); + if (absoluteRequest.startsWith(this.ignorePrefix)) { + return; + } + + const resolved = await this.cachedResolveRequest(absoluteRequest); if (!resolved) { return; } @@ -143,23 +176,17 @@ export class BundleRefsPlugin { return; } - let matchingRef: BundleRef | undefined; for (const ref of eligibleRefs) { - const resolvedEntry = await this.cachedResolveRequest(ref.contextDir, ref.entry); + const resolvedEntry = await this.cachedResolveRefEntry(ref); if (resolved === resolvedEntry) { - matchingRef = ref; - break; + return new BundleRefModule(ref.exportId); } } - if (!matchingRef) { - const bundleId = Array.from(new Set(eligibleRefs.map((r) => r.bundleId))).join(', '); - const publicDir = eligibleRefs.map((r) => r.entry).join(', '); - throw new Error( - `import [${request}] references a non-public export of the [${bundleId}] bundle and must point to one of the public directories: [${publicDir}]` - ); - } - - return matchingRef; + const bundleId = Array.from(new Set(eligibleRefs.map((r) => r.bundleId))).join(', '); + const publicDir = eligibleRefs.map((r) => r.entry).join(', '); + throw new Error( + `import [${request}] references a non-public export of the [${bundleId}] bundle and must point to one of the public directories: [${publicDir}]` + ); } } diff --git a/packages/kbn-optimizer/src/worker/entry_point_creator.ts b/packages/kbn-optimizer/src/worker/entry_point_creator.ts index a613e3e8925a4b..f8f41b2e134229 100644 --- a/packages/kbn-optimizer/src/worker/entry_point_creator.ts +++ b/packages/kbn-optimizer/src/worker/entry_point_creator.ts @@ -17,9 +17,13 @@ * under the License. */ -module.exports = function ({ entries }: { entries: Array<{ importId: string; relPath: string }> }) { - const lines = entries.map(({ importId, relPath }) => [ - `__kbnBundles__.define('${importId}', __webpack_require__, require.resolve('./${relPath}'))`, +module.exports = function ({ + entries, +}: { + entries: Array<{ importId: string; requirePath: string }>; +}) { + const lines = entries.map(({ importId, requirePath }) => [ + `__kbnBundles__.define('${importId}', __webpack_require__, require.resolve('${requirePath}'))`, ]); return { diff --git a/packages/kbn-optimizer/src/worker/run_worker.ts b/packages/kbn-optimizer/src/worker/run_worker.ts index 178637d39ab003..781cf83624a1e4 100644 --- a/packages/kbn-optimizer/src/worker/run_worker.ts +++ b/packages/kbn-optimizer/src/worker/run_worker.ts @@ -17,7 +17,10 @@ * under the License. */ +import { inspect } from 'util'; + import * as Rx from 'rxjs'; +import { take, mergeMap } from 'rxjs/operators'; import { parseBundles, @@ -80,15 +83,38 @@ setInterval(() => { } }, 1000).unref(); +function assertInitMsg(msg: unknown): asserts msg is { args: string[] } { + if (typeof msg !== 'object' || !msg) { + throw new Error(`expected init message to be an object: ${inspect(msg)}`); + } + + const { args } = msg as Record; + if (!args || !Array.isArray(args) || !args.every((a) => typeof a === 'string')) { + throw new Error( + `expected init message to have an 'args' property that's an array of strings: ${inspect(msg)}` + ); + } +} + Rx.defer(() => { - const workerConfig = parseWorkerConfig(process.argv[2]); - const bundles = parseBundles(process.argv[3]); - const bundleRefs = BundleRefs.parseSpec(process.argv[4]); + process.send!('init'); + + return Rx.fromEvent<[unknown]>(process as any, 'message').pipe( + take(1), + mergeMap(([msg]) => { + assertInitMsg(msg); + process.send!('ready'); + + const workerConfig = parseWorkerConfig(msg.args[0]); + const bundles = parseBundles(msg.args[1]); + const bundleRefs = BundleRefs.parseSpec(msg.args[2]); - // set BROWSERSLIST_ENV so that style/babel loaders see it before running compilers - process.env.BROWSERSLIST_ENV = workerConfig.browserslistEnv; + // set BROWSERSLIST_ENV so that style/babel loaders see it before running compilers + process.env.BROWSERSLIST_ENV = workerConfig.browserslistEnv; - return runCompilers(workerConfig, bundles, bundleRefs); + return runCompilers(workerConfig, bundles, bundleRefs); + }) + ); }).subscribe( (msg) => { send(msg); diff --git a/packages/kbn-optimizer/src/worker/webpack.config.ts b/packages/kbn-optimizer/src/worker/webpack.config.ts index e361b186c30e09..11f5544cd9274d 100644 --- a/packages/kbn-optimizer/src/worker/webpack.config.ts +++ b/packages/kbn-optimizer/src/worker/webpack.config.ts @@ -100,10 +100,16 @@ export function getWebpackConfig(bundle: Bundle, bundleRefs: BundleRefs, worker: entries: bundle.publicDirNames.map((name) => { const absolute = Path.resolve(bundle.contextDir, name); const newContext = Path.dirname(ENTRY_CREATOR); - return { - importId: `${bundle.type}/${bundle.id}/${name}`, - relPath: Path.relative(newContext, absolute), - }; + const importId = `${bundle.type}/${bundle.id}/${name}`; + + // relative path from context of the ENTRY_CREATOR, with linux path separators + let requirePath = Path.relative(newContext, absolute).split('\\').join('/'); + if (!requirePath.startsWith('.')) { + // ensure requirePath is identified by node as relative + requirePath = `./${requirePath}`; + } + + return { importId, requirePath }; }), }, }, diff --git a/packages/kbn-pm/dist/index.js b/packages/kbn-pm/dist/index.js index 1600d66ec75589..f6d008c9bf9be4 100644 --- a/packages/kbn-pm/dist/index.js +++ b/packages/kbn-pm/dist/index.js @@ -8846,13 +8846,13 @@ const BootstrapCommand = { if (valid) { _utils_log__WEBPACK_IMPORTED_MODULE_1__["log"].debug(`[${project.name}] cache up to date`); + cachedProjectCount += 1; } caches.set(project, { file, valid }); - cachedProjectCount += 1; } } diff --git a/packages/kbn-pm/src/commands/bootstrap.ts b/packages/kbn-pm/src/commands/bootstrap.ts index 80ccc5daecc564..f8e50a8247856e 100644 --- a/packages/kbn-pm/src/commands/bootstrap.ts +++ b/packages/kbn-pm/src/commands/bootstrap.ts @@ -74,10 +74,10 @@ export const BootstrapCommand: ICommand = { if (valid) { log.debug(`[${project.name}] cache up to date`); + cachedProjectCount += 1; } caches.set(project, { file, valid }); - cachedProjectCount += 1; } } diff --git a/src/dev/code_coverage/ingest_coverage/index_mapping.md b/src/dev/code_coverage/docs/code_coverage_job/kibana_code_coverage_index_mapping.md similarity index 97% rename from src/dev/code_coverage/ingest_coverage/index_mapping.md rename to src/dev/code_coverage/docs/code_coverage_job/kibana_code_coverage_index_mapping.md index c3b934eb35c5e1..66654dd730518e 100644 --- a/src/dev/code_coverage/ingest_coverage/index_mapping.md +++ b/src/dev/code_coverage/docs/code_coverage_job/kibana_code_coverage_index_mapping.md @@ -1,9 +1,12 @@ -# Create index mapping +# Index Mapping + +Execute the following in Kibana Dev Tools. -This is usually done in Kibana's dev tools ui. ``` - "mappings" : { +PUT /kibana_code_coverage +{ + "mappings" : { "properties" : { "@timestamp" : { "type" : "date" @@ -189,6 +192,7 @@ This is usually done in Kibana's dev tools ui. } } } +} ``` _The main portion of the above mapping, is the timestamp-date mapping._ \ No newline at end of file diff --git a/src/dev/code_coverage/ingest_coverage/ingest_code_coverage_readme.md b/src/dev/code_coverage/docs/ingest_code_coverage_readme.md similarity index 52% rename from src/dev/code_coverage/ingest_coverage/ingest_code_coverage_readme.md rename to src/dev/code_coverage/docs/ingest_code_coverage_readme.md index 0670780a7c03ac..174f729180b779 100644 --- a/src/dev/code_coverage/ingest_coverage/ingest_code_coverage_readme.md +++ b/src/dev/code_coverage/docs/ingest_code_coverage_readme.md @@ -1,7 +1,18 @@ -# Convert Code Coverage Json Summary and Send to ES +# Massage and Ingest Code Coverage Json Summary and Send to ES +## Currently, we have 4 indexes +### 2 for the Code Coverage Job +https://kibana-ci.elastic.co/job/elastic+kibana+code-coverage/ +1. kibana_code_coverage +2. kibana_total_code_coverage + +### 2 for the R & D Job +https://kibana-ci.elastic.co/job/elastic+kibana+qa-research/ +1. qa_research_code_coverage +2. qa_research_total_code_coverage + ## How it works It starts with this jenkins pipeline file: @@ -19,6 +30,6 @@ From there, an event stream is created, that massages the data to an output form ## Configuration There is really only one config step. -The index [mapping](src/dev/code_coverage/ingest_coverage/index_mapping.md) for one of +The index [mapping](./code_coverage_job/kibana_code_coverage_index_mapping.md) for one of of the indexes has to be manually created. Currently, we just add it using Kibana's Dev Tools. \ No newline at end of file diff --git a/src/dev/code_coverage/docs/qa_research_job/put_qa_research_code_coverage_with_mapping.png b/src/dev/code_coverage/docs/qa_research_job/put_qa_research_code_coverage_with_mapping.png new file mode 100644 index 00000000000000..c2e4ee4546dfcf Binary files /dev/null and b/src/dev/code_coverage/docs/qa_research_job/put_qa_research_code_coverage_with_mapping.png differ diff --git a/src/dev/code_coverage/docs/qa_research_job/qa_research_index_mapping.md b/src/dev/code_coverage/docs/qa_research_job/qa_research_index_mapping.md new file mode 100644 index 00000000000000..746f81c761fccf --- /dev/null +++ b/src/dev/code_coverage/docs/qa_research_job/qa_research_index_mapping.md @@ -0,0 +1,193 @@ +``` +PUT /qa_research_code_coverage +{ + "mappings" : { + "properties" : { + "@timestamp" : { + "type" : "date" + }, + "BUILD_ID" : { + "type" : "text", + "fields" : { + "keyword" : { + "type" : "keyword", + "ignore_above" : 256 + } + } + }, + "branches" : { + "properties" : { + "covered" : { + "type" : "long" + }, + "pct" : { + "type" : "long" + }, + "skipped" : { + "type" : "long" + }, + "total" : { + "type" : "long" + } + } + }, + "ciRunUrl" : { + "type" : "text", + "fields" : { + "keyword" : { + "type" : "keyword", + "ignore_above" : 256 + } + } + }, + "coveredFilePath" : { + "type" : "text", + "fields" : { + "keyword" : { + "type" : "keyword", + "ignore_above" : 256 + } + } + }, + "functions" : { + "properties" : { + "covered" : { + "type" : "long" + }, + "pct" : { + "type" : "long" + }, + "skipped" : { + "type" : "long" + }, + "total" : { + "type" : "long" + } + } + }, + "isTotal" : { + "type" : "boolean" + }, + "jsonSummaryPath" : { + "type" : "text", + "fields" : { + "keyword" : { + "type" : "keyword", + "ignore_above" : 256 + } + } + }, + "lines" : { + "properties" : { + "covered" : { + "type" : "long" + }, + "pct" : { + "type" : "long" + }, + "skipped" : { + "type" : "long" + }, + "total" : { + "type" : "long" + } + } + }, + "path" : { + "type" : "text", + "fields" : { + "keyword" : { + "type" : "keyword", + "ignore_above" : 256 + } + } + }, + "statements" : { + "properties" : { + "covered" : { + "type" : "long" + }, + "pct" : { + "type" : "long" + }, + "skipped" : { + "type" : "long" + }, + "total" : { + "type" : "long" + } + } + }, + "staticSiteUrl" : { + "type" : "text", + "fields" : { + "keyword" : { + "type" : "keyword", + "ignore_above" : 256 + } + } + }, + "testRunnerType" : { + "type" : "text", + "fields" : { + "keyword" : { + "type" : "keyword", + "ignore_above" : 256 + } + } + }, + "vcs" : { + "properties" : { + "author" : { + "type" : "text", + "fields" : { + "keyword" : { + "type" : "keyword", + "ignore_above" : 256 + } + } + }, + "branch" : { + "type" : "text", + "fields" : { + "keyword" : { + "type" : "keyword", + "ignore_above" : 256 + } + } + }, + "commitMsg" : { + "type" : "text", + "fields" : { + "keyword" : { + "type" : "keyword", + "ignore_above" : 256 + } + } + }, + "sha" : { + "type" : "text", + "fields" : { + "keyword" : { + "type" : "keyword", + "ignore_above" : 256 + } + } + }, + "vcsUrl" : { + "type" : "text", + "fields" : { + "keyword" : { + "type" : "keyword", + "ignore_above" : 256 + } + } + } + } + } + } + } +} +``` + +Execute the above in Kibana Dev Tools, eg: ![Index Mapping Screenshot](./put_qa_research_code_coverage_with_mapping.png "QA Research Code Coverage Index Mapping") \ No newline at end of file diff --git a/src/dev/code_coverage/ingest_coverage/teams_scripted_field.painless b/src/dev/code_coverage/docs/teams_scripted_field.painless similarity index 100% rename from src/dev/code_coverage/ingest_coverage/teams_scripted_field.painless rename to src/dev/code_coverage/docs/teams_scripted_field.painless diff --git a/src/plugins/data/server/search/i_route_handler_search_context.ts b/src/plugins/data/server/field_formats/mocks.ts similarity index 80% rename from src/plugins/data/server/search/i_route_handler_search_context.ts rename to src/plugins/data/server/field_formats/mocks.ts index 9888c774ea104c..ecfa33c86cf162 100644 --- a/src/plugins/data/server/search/i_route_handler_search_context.ts +++ b/src/plugins/data/server/field_formats/mocks.ts @@ -17,9 +17,14 @@ * under the License. */ -import { ISearchGeneric, ISearchCancelGeneric } from './i_search'; +export function createFieldFormatsSetupMock() { + return { + register: jest.fn(), + }; +} -export interface IRouteHandlerSearchContext { - search: ISearchGeneric; - cancel: ISearchCancelGeneric; +export function createFieldFormatsStartMock() { + return { + fieldFormatServiceFactory: jest.fn(), + }; } diff --git a/src/plugins/data/server/index.ts b/src/plugins/data/server/index.ts index 831d23864d2287..16ac59e300237d 100644 --- a/src/plugins/data/server/index.ts +++ b/src/plugins/data/server/index.ts @@ -172,8 +172,10 @@ export { ISearchOptions, IRequestTypesMap, IResponseTypesMap, - ISearchContext, - TSearchStrategyProvider, + ISearchSetup, + ISearchStart, + TStrategyTypes, + ISearchStrategy, getDefaultSearchParams, getTotalLoaded, } from './search'; diff --git a/src/plugins/data/server/search/i_search_context.ts b/src/plugins/data/server/mocks.ts similarity index 60% rename from src/plugins/data/server/search/i_search_context.ts rename to src/plugins/data/server/mocks.ts index 9d9de055d994fd..e2f2298234054b 100644 --- a/src/plugins/data/server/search/i_search_context.ts +++ b/src/plugins/data/server/mocks.ts @@ -17,10 +17,24 @@ * under the License. */ -import { Observable } from 'rxjs'; -import { CoreSetup, SharedGlobalConfig } from '../../../../core/server'; +import { createSearchSetupMock, createSearchStartMock } from './search/mocks'; +import { createFieldFormatsSetupMock, createFieldFormatsStartMock } from './field_formats/mocks'; -export interface ISearchContext { - core: CoreSetup; - config$: Observable; +function createSetupContract() { + return { + search: createSearchSetupMock(), + fieldFormats: createFieldFormatsSetupMock(), + }; } + +function createStartContract() { + return { + search: createSearchStartMock(), + fieldFormats: createFieldFormatsStartMock(), + }; +} + +export const dataPluginMock = { + createSetupContract, + createStartContract, +}; diff --git a/src/plugins/data/server/plugin.ts b/src/plugins/data/server/plugin.ts index 8c9d0df2ed8947..0edce458f1c6b3 100644 --- a/src/plugins/data/server/plugin.ts +++ b/src/plugins/data/server/plugin.ts @@ -20,7 +20,7 @@ import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from '../../../core/server'; import { ConfigSchema } from '../config'; import { IndexPatternsService } from './index_patterns'; -import { ISearchSetup } from './search'; +import { ISearchSetup, ISearchStart } from './search'; import { SearchService } from './search/search_service'; import { QueryService } from './query/query_service'; import { ScriptsService } from './scripts'; @@ -36,6 +36,7 @@ export interface DataPluginSetup { } export interface DataPluginStart { + search: ISearchStart; fieldFormats: FieldFormatsStart; } @@ -59,7 +60,10 @@ export class DataServerPlugin implements Plugin, + { usageCollection }: DataPluginSetupDependencies + ) { this.indexPatterns.setup(core); this.scriptsService.setup(core); this.queryService.setup(core); @@ -69,13 +73,14 @@ export class DataServerPlugin implements Plugin Promise.resolve({ total: 100, loaded: 0 })); -const mockDefaultSearchStrategyProvider = jest.fn(() => - Promise.resolve({ - search: mockDefaultSearch, - }) -); -const mockStrategies: TSearchStrategiesMap = { - [DEFAULT_SEARCH_STRATEGY]: mockDefaultSearchStrategyProvider, -}; - -describe('createApi', () => { - let api: IRouteHandlerSearchContext; - - beforeEach(() => { - api = createApi({ - caller: jest.fn(), - searchStrategies: mockStrategies, - }); - mockDefaultSearchStrategyProvider.mockClear(); - }); - - it('should default to DEFAULT_SEARCH_STRATEGY if none is provided', async () => { - await api.search({ - params: {}, - }); - expect(mockDefaultSearchStrategyProvider).toBeCalled(); - expect(mockDefaultSearch).toBeCalled(); - }); - - it('should throw if no provider is found for the given name', () => { - expect(api.search({}, {}, 'noneByThisName')).rejects.toThrowErrorMatchingInlineSnapshot( - `"No strategy found for noneByThisName"` - ); - }); - - it('logs the response if `debug` is set to `true`', async () => { - const spy = jest.spyOn(console, 'log'); - await api.search({ params: {} }); - - expect(spy).not.toBeCalled(); - - await api.search({ debug: true, params: {} }); - - expect(spy).toBeCalled(); - }); -}); diff --git a/src/plugins/data/server/search/create_api.ts b/src/plugins/data/server/search/create_api.ts deleted file mode 100644 index 00665b21f2ba75..00000000000000 --- a/src/plugins/data/server/search/create_api.ts +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { APICaller } from 'kibana/server'; -import { IRouteHandlerSearchContext } from './i_route_handler_search_context'; -import { DEFAULT_SEARCH_STRATEGY } from '../../common/search'; -import { TSearchStrategiesMap } from './i_search_strategy'; - -export function createApi({ - caller, - searchStrategies, -}: { - searchStrategies: TSearchStrategiesMap; - caller: APICaller; -}) { - const api: IRouteHandlerSearchContext = { - search: async (request, options, strategyName) => { - if (request.debug) { - // eslint-disable-next-line - console.log(JSON.stringify(request, null, 2)); - } - const name = strategyName ?? DEFAULT_SEARCH_STRATEGY; - const strategyProvider = searchStrategies[name]; - if (!strategyProvider) { - throw new Error(`No strategy found for ${strategyName}`); - } - // Give providers access to other search strategies by injecting this function - const strategy = await strategyProvider(caller, api.search); - return strategy.search(request, options); - }, - cancel: async (id, strategyName) => { - const name = strategyName ?? DEFAULT_SEARCH_STRATEGY; - const strategyProvider = searchStrategies[name]; - if (!strategyProvider) { - throw new Error(`No strategy found for ${strategyName}`); - } - const strategy = await strategyProvider(caller, api.search); - return strategy.cancel && strategy.cancel(id); - }, - }; - return api; -} diff --git a/src/plugins/data/server/search/es_search/es_search_strategy.test.ts b/src/plugins/data/server/search/es_search/es_search_strategy.test.ts index c4b8119f9e0958..1155a5491e8f3c 100644 --- a/src/plugins/data/server/search/es_search/es_search_strategy.test.ts +++ b/src/plugins/data/server/search/es_search/es_search_strategy.test.ts @@ -17,11 +17,11 @@ * under the License. */ -import { coreMock, pluginInitializerContextConfigMock } from '../../../../../core/server/mocks'; +import { RequestHandlerContext } from '../../../../../core/server'; +import { pluginInitializerContextConfigMock } from '../../../../../core/server/mocks'; import { esSearchStrategyProvider } from './es_search_strategy'; describe('ES search strategy', () => { - const mockCoreSetup = coreMock.createSetup(); const mockApiCaller = jest.fn().mockResolvedValue({ _shards: { total: 10, @@ -30,39 +30,26 @@ describe('ES search strategy', () => { successful: 7, }, }); - const mockSearch = jest.fn(); + const mockContext = { + core: { elasticsearch: { legacy: { client: { callAsCurrentUser: mockApiCaller } } } }, + }; const mockConfig$ = pluginInitializerContextConfigMock({}).legacy.globalConfig$; beforeEach(() => { mockApiCaller.mockClear(); - mockSearch.mockClear(); }); - it('returns a strategy with `search`', () => { - const esSearch = esSearchStrategyProvider( - { - core: mockCoreSetup, - config$: mockConfig$, - }, - mockApiCaller, - mockSearch - ); + it('returns a strategy with `search`', async () => { + const esSearch = await esSearchStrategyProvider(mockConfig$); expect(typeof esSearch.search).toBe('function'); }); it('calls the API caller with the params with defaults', async () => { const params = { index: 'logstash-*' }; - const esSearch = esSearchStrategyProvider( - { - core: mockCoreSetup, - config$: mockConfig$, - }, - mockApiCaller, - mockSearch - ); + const esSearch = await esSearchStrategyProvider(mockConfig$); - await esSearch.search({ params }); + await esSearch.search((mockContext as unknown) as RequestHandlerContext, { params }); expect(mockApiCaller).toBeCalled(); expect(mockApiCaller.mock.calls[0][0]).toBe('search'); @@ -76,16 +63,9 @@ describe('ES search strategy', () => { it('calls the API caller with overridden defaults', async () => { const params = { index: 'logstash-*', ignoreUnavailable: false, timeout: '1000ms' }; - const esSearch = esSearchStrategyProvider( - { - core: mockCoreSetup, - config$: mockConfig$, - }, - mockApiCaller, - mockSearch - ); + const esSearch = await esSearchStrategyProvider(mockConfig$); - await esSearch.search({ params }); + await esSearch.search((mockContext as unknown) as RequestHandlerContext, { params }); expect(mockApiCaller).toBeCalled(); expect(mockApiCaller.mock.calls[0][0]).toBe('search'); @@ -97,16 +77,11 @@ describe('ES search strategy', () => { it('returns total, loaded, and raw response', async () => { const params = { index: 'logstash-*' }; - const esSearch = esSearchStrategyProvider( - { - core: mockCoreSetup, - config$: mockConfig$, - }, - mockApiCaller, - mockSearch - ); + const esSearch = await esSearchStrategyProvider(mockConfig$); - const response = await esSearch.search({ params }); + const response = await esSearch.search((mockContext as unknown) as RequestHandlerContext, { + params, + }); expect(response).toHaveProperty('total'); expect(response).toHaveProperty('loaded'); diff --git a/src/plugins/data/server/search/es_search/es_search_strategy.ts b/src/plugins/data/server/search/es_search/es_search_strategy.ts index 47cad7aa6b4d75..db08ddf9208185 100644 --- a/src/plugins/data/server/search/es_search/es_search_strategy.ts +++ b/src/plugins/data/server/search/es_search/es_search_strategy.ts @@ -17,19 +17,18 @@ * under the License. */ import { first } from 'rxjs/operators'; -import { APICaller } from 'kibana/server'; +import { RequestHandlerContext, SharedGlobalConfig } from 'kibana/server'; import { SearchResponse } from 'elasticsearch'; +import { Observable } from 'rxjs'; import { ES_SEARCH_STRATEGY } from '../../../common/search'; -import { ISearchStrategy, TSearchStrategyProvider } from '../i_search_strategy'; -import { getDefaultSearchParams, getTotalLoaded, ISearchContext } from '..'; +import { ISearchStrategy, getDefaultSearchParams, getTotalLoaded } from '..'; -export const esSearchStrategyProvider: TSearchStrategyProvider = ( - context: ISearchContext, - caller: APICaller +export const esSearchStrategyProvider = ( + config$: Observable ): ISearchStrategy => { return { - search: async (request, options) => { - const config = await context.config$.pipe(first()).toPromise(); + search: async (context: RequestHandlerContext, request, options) => { + const config = await config$.pipe(first()).toPromise(); const defaultParams = getDefaultSearchParams(config); // Only default index pattern type is supported here. @@ -42,7 +41,12 @@ export const esSearchStrategyProvider: TSearchStrategyProvider; + + const rawResponse = (await context.core.elasticsearch.legacy.client.callAsCurrentUser( + 'search', + params, + options + )) as SearchResponse; // The above query will either complete or timeout and throw an error. // There is no progress indication on this api. diff --git a/src/plugins/data/server/search/i_search.ts b/src/plugins/data/server/search/i_search.ts deleted file mode 100644 index fa4aa72ac7287e..00000000000000 --- a/src/plugins/data/server/search/i_search.ts +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { IKibanaSearchResponse, IKibanaSearchRequest } from '../../common/search'; -import { TStrategyTypes } from './strategy_types'; -import { ES_SEARCH_STRATEGY, IEsSearchResponse } from '../../common/search/es_search'; -import { IEsSearchRequest } from './es_search'; - -export interface ISearchOptions { - signal?: AbortSignal; -} - -export interface IRequestTypesMap { - [ES_SEARCH_STRATEGY]: IEsSearchRequest; - [key: string]: IKibanaSearchRequest; -} - -export interface IResponseTypesMap { - [ES_SEARCH_STRATEGY]: IEsSearchResponse; - [key: string]: IKibanaSearchResponse; -} - -export type ISearchGeneric = ( - request: IRequestTypesMap[T], - options?: ISearchOptions, - strategy?: T -) => Promise; - -export type ISearchCancelGeneric = ( - id: string, - strategy?: T -) => Promise; - -export type ISearch = ( - request: IRequestTypesMap[T], - options?: ISearchOptions -) => Promise; - -export type ISearchCancel = (id: string) => Promise; diff --git a/src/plugins/data/server/search/i_search_setup.ts b/src/plugins/data/server/search/i_search_setup.ts deleted file mode 100644 index e4a4d501412018..00000000000000 --- a/src/plugins/data/server/search/i_search_setup.ts +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { IContextProvider } from 'kibana/server'; -import { ISearchContext } from './i_search_context'; -import { TRegisterSearchStrategyProvider, TSearchStrategyProvider } from './i_search_strategy'; - -/** - * The setup contract exposed by the Search plugin exposes the search strategy extension - * point. - */ -export interface ISearchSetup { - registerSearchStrategyContext: ( - pluginId: symbol, - strategyName: TContextName, - provider: IContextProvider, TContextName> - ) => void; - - /** - * Extension point exposed for other plugins to register their own search - * strategies. - */ - registerSearchStrategyProvider: TRegisterSearchStrategyProvider; -} diff --git a/src/plugins/data/server/search/i_search_strategy.ts b/src/plugins/data/server/search/i_search_strategy.ts deleted file mode 100644 index 9b405034f883f7..00000000000000 --- a/src/plugins/data/server/search/i_search_strategy.ts +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { APICaller } from 'kibana/server'; -import { ISearch, ISearchCancel, ISearchGeneric } from './i_search'; -import { TStrategyTypes } from './strategy_types'; -import { ISearchContext } from './i_search_context'; - -/** - * Search strategy interface contains a search method that takes in - * a request and returns a promise that resolves to a response. - */ -export interface ISearchStrategy { - search: ISearch; - cancel?: ISearchCancel; -} - -/** - * Search strategy provider creates an instance of a search strategy with the request - * handler context bound to it. This way every search strategy can use - * whatever information they require from the request context. - */ -export type TSearchStrategyProviderEnhanced = ( - caller: APICaller, - search: ISearchGeneric -) => Promise>; - -/** - * Search strategy provider creates an instance of a search strategy with the request - * handler context bound to it. This way every search strategy can use - * whatever information they require from the request context. - */ -export type TSearchStrategyProvider = ( - context: ISearchContext, - caller: APICaller, - search: ISearchGeneric -) => ISearchStrategy; - -/** - * Extension point exposed for other plugins to register their own search - * strategies. - */ -export type TRegisterSearchStrategyProvider = ( - opaqueId: symbol, - name: T, - searchStrategyProvider: TSearchStrategyProvider -) => void; - -export type TSearchStrategiesMap = { - [K in TStrategyTypes]?: TSearchStrategyProviderEnhanced; -}; diff --git a/src/plugins/data/server/search/index.ts b/src/plugins/data/server/search/index.ts index e08eba1cad8318..882f56e83d4ca2 100644 --- a/src/plugins/data/server/search/index.ts +++ b/src/plugins/data/server/search/index.ts @@ -17,20 +17,16 @@ * under the License. */ -export { ISearchSetup } from './i_search_setup'; - -export { ISearchContext } from './i_search_context'; - export { ISearch, ISearchCancel, ISearchOptions, IRequestTypesMap, IResponseTypesMap, -} from './i_search'; - -export { TStrategyTypes } from './strategy_types'; - -export { TSearchStrategyProvider } from './i_search_strategy'; + ISearchSetup, + ISearchStart, + TStrategyTypes, + ISearchStrategy, +} from './types'; export { getDefaultSearchParams, getTotalLoaded } from './es_search'; diff --git a/src/plugins/data/server/search/mocks.ts b/src/plugins/data/server/search/mocks.ts index 136e7a1d580c9d..0aab466a9a0d9e 100644 --- a/src/plugins/data/server/search/mocks.ts +++ b/src/plugins/data/server/search/mocks.ts @@ -17,10 +17,14 @@ * under the License. */ -export const searchSetupMock = { - registerSearchStrategyContext: jest.fn(), - registerSearchStrategyProvider: jest.fn(), - __LEGACY: { - search: jest.fn(), - }, -}; +export function createSearchSetupMock() { + return { + registerSearchStrategy: jest.fn(), + }; +} + +export function createSearchStartMock() { + return { + getSearchStrategy: jest.fn(), + }; +} diff --git a/src/plugins/data/server/search/routes.test.ts b/src/plugins/data/server/search/routes.test.ts index f5e6507d977cd8..4ef67de93e4549 100644 --- a/src/plugins/data/server/search/routes.test.ts +++ b/src/plugins/data/server/search/routes.test.ts @@ -17,36 +17,26 @@ * under the License. */ -import { httpServiceMock, httpServerMock } from '../../../../../src/core/server/mocks'; +import { CoreSetup, RequestHandlerContext } from '../../../../../src/core/server'; +import { coreMock, httpServerMock } from '../../../../../src/core/server/mocks'; import { registerSearchRoute } from './routes'; -import { IRouter, ScopedClusterClient, RequestHandlerContext } from 'kibana/server'; +import { DataPluginStart } from '../plugin'; +import { dataPluginMock } from '../mocks'; describe('Search service', () => { - let routerMock: jest.Mocked; + let mockDataStart: MockedKeys; + let mockCoreSetup: MockedKeys>; beforeEach(() => { - routerMock = httpServiceMock.createRouter(); - }); - - it('registers a post route', async () => { - registerSearchRoute(routerMock); - expect(routerMock.post).toBeCalled(); + mockDataStart = dataPluginMock.createStartContract(); + mockCoreSetup = coreMock.createSetup({ pluginStartContract: mockDataStart }); }); it('handler calls context.search.search with the given request and strategy', async () => { const mockSearch = jest.fn().mockResolvedValue('yay'); - const mockContext = { - core: { - elasticsearch: { - legacy: { - client: {} as ScopedClusterClient, - }, - }, - }, - search: { - search: mockSearch, - }, - }; + mockDataStart.search.getSearchStrategy.mockReturnValueOnce({ search: mockSearch }); + + const mockContext = {}; const mockBody = { params: {} }; const mockParams = { strategy: 'foo' }; const mockRequest = httpServerMock.createKibanaRequest({ @@ -55,13 +45,15 @@ describe('Search service', () => { }); const mockResponse = httpServerMock.createResponseFactory(); - registerSearchRoute(routerMock); - const handler = routerMock.post.mock.calls[0][1]; + registerSearchRoute(mockCoreSetup); + + const mockRouter = mockCoreSetup.http.createRouter.mock.results[0].value; + const handler = mockRouter.post.mock.calls[0][1]; await handler((mockContext as unknown) as RequestHandlerContext, mockRequest, mockResponse); + expect(mockDataStart.search.getSearchStrategy.mock.calls[0][0]).toBe(mockParams.strategy); expect(mockSearch).toBeCalled(); - expect(mockSearch.mock.calls[0][0]).toStrictEqual(mockBody); - expect(mockSearch.mock.calls[0][2]).toBe(mockParams.strategy); + expect(mockSearch.mock.calls[0][1]).toStrictEqual(mockBody); expect(mockResponse.ok).toBeCalled(); expect(mockResponse.ok.mock.calls[0][0]).toEqual({ body: 'yay' }); }); @@ -73,18 +65,9 @@ describe('Search service', () => { error: 'oops', }, }); - const mockContext = { - core: { - elasticsearch: { - legacy: { - client: {} as ScopedClusterClient, - }, - }, - }, - search: { - search: mockSearch, - }, - }; + mockDataStart.search.getSearchStrategy.mockReturnValueOnce({ search: mockSearch }); + + const mockContext = {}; const mockBody = { params: {} }; const mockParams = { strategy: 'foo' }; const mockRequest = httpServerMock.createKibanaRequest({ @@ -93,13 +76,15 @@ describe('Search service', () => { }); const mockResponse = httpServerMock.createResponseFactory(); - registerSearchRoute(routerMock); - const handler = routerMock.post.mock.calls[0][1]; + registerSearchRoute(mockCoreSetup); + + const mockRouter = mockCoreSetup.http.createRouter.mock.results[0].value; + const handler = mockRouter.post.mock.calls[0][1]; await handler((mockContext as unknown) as RequestHandlerContext, mockRequest, mockResponse); + expect(mockDataStart.search.getSearchStrategy.mock.calls[0][0]).toBe(mockParams.strategy); expect(mockSearch).toBeCalled(); - expect(mockSearch.mock.calls[0][0]).toStrictEqual(mockBody); - expect(mockSearch.mock.calls[0][2]).toBe(mockParams.strategy); + expect(mockSearch.mock.calls[0][1]).toStrictEqual(mockBody); expect(mockResponse.customError).toBeCalled(); const error: any = mockResponse.customError.mock.calls[0][0]; expect(error.body.message).toBe('oh no'); diff --git a/src/plugins/data/server/search/routes.ts b/src/plugins/data/server/search/routes.ts index b90d7d4ff80cea..7b6c045b0908c4 100644 --- a/src/plugins/data/server/search/routes.ts +++ b/src/plugins/data/server/search/routes.ts @@ -18,10 +18,13 @@ */ import { schema } from '@kbn/config-schema'; -import { IRouter } from '../../../../core/server'; +import { CoreSetup } from '../../../../core/server'; import { getRequestAbortedSignal } from '../lib'; +import { DataPluginStart } from '../plugin'; + +export function registerSearchRoute(core: CoreSetup): void { + const router = core.http.createRouter(); -export function registerSearchRoute(router: IRouter): void { router.post( { path: '/internal/search/{strategy}', @@ -38,8 +41,11 @@ export function registerSearchRoute(router: IRouter): void { const { strategy } = request.params; const signal = getRequestAbortedSignal(request.events.aborted$); + const [, , selfStart] = await core.getStartServices(); + const searchStrategy = selfStart.search.getSearchStrategy(strategy); + try { - const response = await context.search!.search(searchRequest, { signal }, strategy); + const response = await searchStrategy.search(context, searchRequest, { signal }); return res.ok({ body: response }); } catch (err) { return res.customError({ @@ -69,8 +75,13 @@ export function registerSearchRoute(router: IRouter): void { }, async (context, request, res) => { const { strategy, id } = request.params; + + const [, , selfStart] = await core.getStartServices(); + const searchStrategy = selfStart.search.getSearchStrategy(strategy); + if (!searchStrategy.cancel) return res.ok(); + try { - await context.search!.cancel(id, strategy); + await searchStrategy.cancel(context, id); return res.ok(); } catch (err) { return res.customError({ diff --git a/src/plugins/data/server/search/search_service.test.ts b/src/plugins/data/server/search/search_service.test.ts index fa659756c12732..25143fa09e6bff 100644 --- a/src/plugins/data/server/search/search_service.test.ts +++ b/src/plugins/data/server/search/search_service.test.ts @@ -21,27 +21,28 @@ import { coreMock } from '../../../../core/server/mocks'; import { SearchService } from './search_service'; import { CoreSetup } from '../../../../core/server'; - -const mockSearchApi = { search: jest.fn() }; -jest.mock('./create_api', () => ({ - createApi: () => mockSearchApi, -})); +import { DataPluginStart } from '../plugin'; describe('Search service', () => { let plugin: SearchService; - let mockCoreSetup: MockedKeys; + let mockCoreSetup: MockedKeys>; beforeEach(() => { plugin = new SearchService(coreMock.createPluginInitializerContext({})); mockCoreSetup = coreMock.createSetup(); - mockSearchApi.search.mockClear(); }); describe('setup()', () => { it('exposes proper contract', async () => { const setup = plugin.setup(mockCoreSetup); - expect(setup).toHaveProperty('registerSearchStrategyContext'); - expect(setup).toHaveProperty('registerSearchStrategyProvider'); + expect(setup).toHaveProperty('registerSearchStrategy'); + }); + }); + + describe('start()', () => { + it('exposes proper contract', async () => { + const setup = plugin.start(); + expect(setup).toHaveProperty('getSearchStrategy'); }); }); }); diff --git a/src/plugins/data/server/search/search_service.ts b/src/plugins/data/server/search/search_service.ts index 1c267c32ebc37b..df809b425eb9ee 100644 --- a/src/plugins/data/server/search/search_service.ts +++ b/src/plugins/data/server/search/search_service.ts @@ -17,82 +17,52 @@ * under the License. */ +import { Plugin, PluginInitializerContext, CoreSetup } from '../../../../core/server'; import { - PluginInitializerContext, - Plugin, - CoreSetup, - IContextContainer, -} from '../../../../core/server'; -import { registerSearchRoute } from './routes'; -import { ISearchSetup } from './i_search_setup'; -import { createApi } from './create_api'; -import { + ISearchSetup, + ISearchStart, TSearchStrategiesMap, - TSearchStrategyProvider, - TRegisterSearchStrategyProvider, -} from './i_search_strategy'; -import { IRouteHandlerSearchContext } from './i_route_handler_search_context'; + TRegisterSearchStrategy, + TGetSearchStrategy, +} from './types'; +import { registerSearchRoute } from './routes'; import { ES_SEARCH_STRATEGY, esSearchStrategyProvider } from './es_search'; - import { searchSavedObjectType } from '../saved_objects'; +import { DataPluginStart } from '../plugin'; -declare module 'kibana/server' { - interface RequestHandlerContext { - search?: IRouteHandlerSearchContext; - } -} - -export class SearchService implements Plugin { +export class SearchService implements Plugin { private searchStrategies: TSearchStrategiesMap = {}; - private contextContainer?: IContextContainer>; - constructor(private initializerContext: PluginInitializerContext) {} - public setup(core: CoreSetup): ISearchSetup { - const router = core.http.createRouter(); - registerSearchRoute(router); - - this.contextContainer = core.context.createContextContainer(); - + public setup(core: CoreSetup): ISearchSetup { core.savedObjects.registerType(searchSavedObjectType); - core.http.registerRouteHandlerContext<'search'>('search', (context) => { - return createApi({ - caller: context.core.elasticsearch.legacy.client.callAsCurrentUser, - searchStrategies: this.searchStrategies, - }); - }); - - const registerSearchStrategyProvider: TRegisterSearchStrategyProvider = ( - plugin, - name, - strategyProvider - ) => { - this.searchStrategies[name] = this.contextContainer!.createHandler(plugin, strategyProvider); - }; - - const api: ISearchSetup = { - registerSearchStrategyContext: this.contextContainer!.registerContext, - registerSearchStrategyProvider, - }; - - api.registerSearchStrategyContext(this.initializerContext.opaqueId, 'core', () => core); - api.registerSearchStrategyContext( - this.initializerContext.opaqueId, - 'config$', - () => this.initializerContext.config.legacy.globalConfig$ - ); - - api.registerSearchStrategyProvider( - this.initializerContext.opaqueId, + this.registerSearchStrategy( ES_SEARCH_STRATEGY, - esSearchStrategyProvider + esSearchStrategyProvider(this.initializerContext.config.legacy.globalConfig$) ); - return api; + registerSearchRoute(core); + + return { registerSearchStrategy: this.registerSearchStrategy }; + } + + public start(): ISearchStart { + return { getSearchStrategy: this.getSearchStrategy }; } - public start() {} public stop() {} + + private registerSearchStrategy: TRegisterSearchStrategy = (name, strategy) => { + this.searchStrategies[name] = strategy; + }; + + private getSearchStrategy: TGetSearchStrategy = (name) => { + const strategy = this.searchStrategies[name]; + if (!strategy) { + throw new Error(`Search strategy ${name} not found`); + } + return strategy; + }; } diff --git a/src/plugins/data/server/search/strategy_types.ts b/src/plugins/data/server/search/strategy_types.ts deleted file mode 100644 index 252e0c8f9e6c9d..00000000000000 --- a/src/plugins/data/server/search/strategy_types.ts +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { ES_SEARCH_STRATEGY } from '../../common/search/es_search'; - -/** - * Contains all known strategy type identifiers that will be used to map to - * request and response shapes. Plugins that wish to add their own custom search - * strategies should extend this type via: - * - * const MY_STRATEGY = 'MY_STRATEGY'; - * - * declare module 'src/plugins/search/server' { - * export interface IRequestTypesMap { - * [MY_STRATEGY]: IMySearchRequest; - * } - * - * export interface IResponseTypesMap { - * [MY_STRATEGY]: IMySearchResponse - * } - * } - */ -export type TStrategyTypes = typeof ES_SEARCH_STRATEGY | string; diff --git a/src/plugins/data/server/search/types.ts b/src/plugins/data/server/search/types.ts new file mode 100644 index 00000000000000..dea325cc063bbf --- /dev/null +++ b/src/plugins/data/server/search/types.ts @@ -0,0 +1,111 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { RequestHandlerContext } from '../../../../core/server'; +import { IKibanaSearchResponse, IKibanaSearchRequest } from '../../common/search'; +import { ES_SEARCH_STRATEGY, IEsSearchRequest, IEsSearchResponse } from './es_search'; + +export interface ISearchSetup { + /** + * Extension point exposed for other plugins to register their own search + * strategies. + */ + registerSearchStrategy: TRegisterSearchStrategy; +} + +export interface ISearchStart { + /** + * Get other registered search strategies. For example, if a new strategy needs to use the + * already-registered ES search strategy, it can use this function to accomplish that. + */ + getSearchStrategy: TGetSearchStrategy; +} + +export interface ISearchOptions { + /** + * An `AbortSignal` that allows the caller of `search` to abort a search request. + */ + signal?: AbortSignal; +} + +/** + * Contains all known strategy type identifiers that will be used to map to + * request and response shapes. Plugins that wish to add their own custom search + * strategies should extend this type via: + * + * const MY_STRATEGY = 'MY_STRATEGY'; + * + * declare module 'src/plugins/search/server' { + * export interface IRequestTypesMap { + * [MY_STRATEGY]: IMySearchRequest; + * } + * + * export interface IResponseTypesMap { + * [MY_STRATEGY]: IMySearchResponse + * } + * } + */ +export type TStrategyTypes = typeof ES_SEARCH_STRATEGY | string; + +/** + * The map of search strategy IDs to the corresponding request type definitions. + */ +export interface IRequestTypesMap { + [ES_SEARCH_STRATEGY]: IEsSearchRequest; + [key: string]: IKibanaSearchRequest; +} + +/** + * The map of search strategy IDs to the corresponding response type definitions. + */ +export interface IResponseTypesMap { + [ES_SEARCH_STRATEGY]: IEsSearchResponse; + [key: string]: IKibanaSearchResponse; +} + +export type ISearch = ( + context: RequestHandlerContext, + request: IRequestTypesMap[T], + options?: ISearchOptions +) => Promise; + +export type ISearchCancel = ( + context: RequestHandlerContext, + id: string +) => Promise; + +/** + * Search strategy interface contains a search method that takes in a request and returns a promise + * that resolves to a response. + */ +export interface ISearchStrategy { + search: ISearch; + cancel?: ISearchCancel; +} + +export type TRegisterSearchStrategy = ( + name: T, + searchStrategy: ISearchStrategy +) => void; + +export type TGetSearchStrategy = (name: T) => ISearchStrategy; + +export type TSearchStrategiesMap = { + [K in TStrategyTypes]?: ISearchStrategy; +}; diff --git a/src/plugins/data/server/server.api.md b/src/plugins/data/server/server.api.md index 2d4185415b9d5d..d825153a7aa124 100644 --- a/src/plugins/data/server/server.api.md +++ b/src/plugins/data/server/server.api.md @@ -48,7 +48,6 @@ import { GetResponse } from 'elasticsearch'; import { GetScriptParams } from 'elasticsearch'; import { GetSourceParams } from 'elasticsearch'; import { GetTemplateParams } from 'elasticsearch'; -import { IContextProvider as IContextProvider_2 } from 'kibana/server'; import { IncomingHttpHeaders } from 'http'; import { IndexDocumentParams } from 'elasticsearch'; import { IndicesAnalyzeParams } from 'elasticsearch'; @@ -490,7 +489,7 @@ export class IndexPatternsFetcher { // Warning: (ae-missing-release-tag) "IRequestTypesMap" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) +// @public export interface IRequestTypesMap { // Warning: (ae-forgotten-export) The symbol "IKibanaSearchRequest" needs to be exported by the entry point index.d.ts // @@ -505,7 +504,7 @@ export interface IRequestTypesMap { // Warning: (ae-missing-release-tag) "IResponseTypesMap" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) +// @public export interface IResponseTypesMap { // Warning: (ae-forgotten-export) The symbol "IKibanaSearchResponse" needs to be exported by the entry point index.d.ts // @@ -517,35 +516,48 @@ export interface IResponseTypesMap { [ES_SEARCH_STRATEGY]: IEsSearchResponse; } -// Warning: (ae-forgotten-export) The symbol "TStrategyTypes" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "RequestHandlerContext" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "ISearch" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export type ISearch = (request: IRequestTypesMap[T], options?: ISearchOptions) => Promise; +export type ISearch = (context: RequestHandlerContext, request: IRequestTypesMap[T], options?: ISearchOptions) => Promise; // Warning: (ae-missing-release-tag) "ISearchCancel" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export type ISearchCancel = (id: string) => Promise; +export type ISearchCancel = (context: RequestHandlerContext, id: string) => Promise; -// Warning: (ae-missing-release-tag) "ISearchContext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// Warning: (ae-missing-release-tag) "ISearchOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export interface ISearchContext { - // (undocumented) - config$: Observable; - // Warning: (ae-forgotten-export) The symbol "CoreSetup" needs to be exported by the entry point index.d.ts - // - // (undocumented) - core: CoreSetup; +export interface ISearchOptions { + signal?: AbortSignal; } -// Warning: (ae-missing-release-tag) "ISearchOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// Warning: (ae-missing-release-tag) "ISearchSetup" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export interface ISearchOptions { +export interface ISearchSetup { + // Warning: (ae-forgotten-export) The symbol "TRegisterSearchStrategy" needs to be exported by the entry point index.d.ts + registerSearchStrategy: TRegisterSearchStrategy; +} + +// Warning: (ae-missing-release-tag) "ISearchStart" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface ISearchStart { + // Warning: (ae-forgotten-export) The symbol "TGetSearchStrategy" needs to be exported by the entry point index.d.ts + getSearchStrategy: TGetSearchStrategy; +} + +// Warning: (ae-missing-release-tag) "ISearchStrategy" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export interface ISearchStrategy { // (undocumented) - signal?: AbortSignal; + cancel?: ISearchCancel; + // (undocumented) + search: ISearch; } // @public (undocumented) @@ -612,21 +624,23 @@ export function parseInterval(interval: string): moment.Duration | null; export class Plugin implements Plugin_2 { // Warning: (ae-forgotten-export) The symbol "PluginInitializerContext" needs to be exported by the entry point index.d.ts constructor(initializerContext: PluginInitializerContext); + // Warning: (ae-forgotten-export) The symbol "CoreSetup" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "DataPluginSetupDependencies" needs to be exported by the entry point index.d.ts // // (undocumented) - setup(core: CoreSetup, { usageCollection }: DataPluginSetupDependencies): { + setup(core: CoreSetup, { usageCollection }: DataPluginSetupDependencies): { + search: ISearchSetup; fieldFormats: { register: (customFieldFormat: import("../public").FieldFormatInstanceType) => number; }; - search: ISearchSetup; }; // Warning: (ae-forgotten-export) The symbol "CoreStart" needs to be exported by the entry point index.d.ts // // (undocumented) start(core: CoreStart): { + search: ISearchStart; fieldFormats: { - fieldFormatServiceFactory: (uiSettings: import("kibana/server").IUiSettingsClient) => Promise; + fieldFormatServiceFactory: (uiSettings: import("../../../core/server").IUiSettingsClient) => Promise; }; }; // (undocumented) @@ -656,6 +670,8 @@ export interface PluginStart { // // (undocumented) fieldFormats: FieldFormatsStart; + // (undocumented) + search: ISearchStart; } // Warning: (ae-missing-release-tag) "Query" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -713,12 +729,10 @@ export interface TimeRange { to: string; } -// Warning: (ae-forgotten-export) The symbol "ISearchGeneric" needs to be exported by the entry point index.d.ts -// Warning: (ae-forgotten-export) The symbol "ISearchStrategy" needs to be exported by the entry point index.d.ts -// Warning: (ae-missing-release-tag) "TSearchStrategyProvider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// Warning: (ae-missing-release-tag) "TStrategyTypes" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public -export type TSearchStrategyProvider = (context: ISearchContext, caller: APICaller_2, search: ISearchGeneric) => ISearchStrategy; +export type TStrategyTypes = typeof ES_SEARCH_STRATEGY | string; // Warning: (ae-missing-release-tag) "UI_SETTINGS" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -778,13 +792,12 @@ export const UI_SETTINGS: { // src/plugins/data/server/index.ts:103:26 - (ae-forgotten-export) The symbol "TruncateFormat" needs to be exported by the entry point index.d.ts // src/plugins/data/server/index.ts:131:27 - (ae-forgotten-export) The symbol "isFilterable" needs to be exported by the entry point index.d.ts // src/plugins/data/server/index.ts:131:27 - (ae-forgotten-export) The symbol "isNestedField" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:184:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:185:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:186:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:187:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:188:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:191:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/plugin.ts:66:14 - (ae-forgotten-export) The symbol "ISearchSetup" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:186:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:187:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:188:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:189:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:190:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:193:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts // (No @packageDocumentation comment for this package) diff --git a/test/plugin_functional/test_suites/core_plugins/legacy_plugins.ts b/test/plugin_functional/test_suites/core_plugins/legacy_plugins.ts index cb3a24375607e2..c9274c867df837 100644 --- a/test/plugin_functional/test_suites/core_plugins/legacy_plugins.ts +++ b/test/plugin_functional/test_suites/core_plugins/legacy_plugins.ts @@ -39,7 +39,7 @@ export default function ({ getService, getPageObjects }: PluginFunctionalProvide await supertest .get('/api/np-context-in-legacy') .expect(200) - .expect(JSON.stringify({ contexts: ['core', 'search', 'pluginA'] })); + .expect(JSON.stringify({ contexts: ['core', 'pluginA'] })); }); }); diff --git a/vars/getCheckoutInfo.groovy b/vars/getCheckoutInfo.groovy index 7a3a7a9d2eccc7..32a7b054bfd151 100644 --- a/vars/getCheckoutInfo.groovy +++ b/vars/getCheckoutInfo.groovy @@ -22,10 +22,14 @@ def call(branchOverride) { ).trim() if (repoInfo.targetBranch) { - sh( - script: "git fetch origin ${repoInfo.targetBranch}", - label: "fetch latest from '${repoInfo.targetBranch}' at origin" - ) + // Try to clone fetch from Github up to 8 times, waiting 15 secs between attempts + retryWithDelay(8, 15) { + sh( + script: "git fetch origin ${repoInfo.targetBranch}", + label: "fetch latest from '${repoInfo.targetBranch}' at origin" + ) + } + repoInfo.mergeBase = sh( script: "git merge-base HEAD FETCH_HEAD", label: "determining merge point with '${repoInfo.targetBranch}' at origin", diff --git a/x-pack/plugins/apm/server/lib/service_map/get_service_anomalies.ts b/x-pack/plugins/apm/server/lib/service_map/get_service_anomalies.ts index ac76464e2f2e8f..900141e9040ae1 100644 --- a/x-pack/plugins/apm/server/lib/service_map/get_service_anomalies.ts +++ b/x-pack/plugins/apm/server/lib/service_map/get_service_anomalies.ts @@ -8,12 +8,33 @@ import { leftJoin } from '../../../common/utils/left_join'; import { Job as AnomalyDetectionJob } from '../../../../ml/server'; import { PromiseReturnType } from '../../../typings/common'; import { IEnvOptions } from './get_service_map'; +import { Setup } from '../helpers/setup_request'; import { APM_ML_JOB_GROUP_NAME, encodeForMlApi, } from '../../../common/ml_job_constants'; +async function getApmAnomalyDetectionJobs( + setup: Setup +): Promise { + const { ml } = setup; + + if (!ml) { + return []; + } + try { + const { jobs } = await ml.anomalyDetectors.jobs(APM_ML_JOB_GROUP_NAME); + return jobs; + } catch (error) { + if (error.statusCode === 404) { + return []; + } + throw error; + } +} + type ApmMlJobCategory = NonNullable>; + export const getApmMlJobCategory = ( mlJob: AnomalyDetectionJob, serviceNames: string[] @@ -62,7 +83,10 @@ export async function getServiceAnomalies( return []; } - const { jobs: apmMlJobs } = await ml.anomalyDetectors.jobs('apm'); + const apmMlJobs = await getApmAnomalyDetectionJobs(options.setup); + if (apmMlJobs.length === 0) { + return []; + } const apmMlJobCategories = apmMlJobs .map((job) => getApmMlJobCategory(job, serviceNames)) .filter( diff --git a/x-pack/plugins/data_enhanced/server/plugin.ts b/x-pack/plugins/data_enhanced/server/plugin.ts index a27a73431574b5..4f6756231912cc 100644 --- a/x-pack/plugins/data_enhanced/server/plugin.ts +++ b/x-pack/plugins/data_enhanced/server/plugin.ts @@ -22,10 +22,9 @@ export class EnhancedDataServerPlugin implements Plugin { - const mockCoreSetup = coreMock.createSetup(); const mockApiCaller = jest.fn(); - const mockSearch = jest.fn(); + const mockContext = { + core: { elasticsearch: { legacy: { client: { callAsCurrentUser: mockApiCaller } } } }, + }; const mockConfig$ = pluginInitializerContextConfigMock({}).legacy.globalConfig$; beforeEach(() => { mockApiCaller.mockClear(); - mockSearch.mockClear(); }); - it('returns a strategy with `search`', () => { - const esSearch = enhancedEsSearchStrategyProvider( - { - core: mockCoreSetup, - config$: mockConfig$, - }, - mockApiCaller, - mockSearch - ); + it('returns a strategy with `search`', async () => { + const esSearch = await enhancedEsSearchStrategyProvider(mockConfig$); expect(typeof esSearch.search).toBe('function'); }); @@ -56,16 +50,9 @@ describe('ES search strategy', () => { mockApiCaller.mockResolvedValueOnce(mockAsyncResponse); const params = { index: 'logstash-*', body: { query: {} } }; - const esSearch = enhancedEsSearchStrategyProvider( - { - core: mockCoreSetup, - config$: mockConfig$, - }, - mockApiCaller, - mockSearch - ); + const esSearch = await enhancedEsSearchStrategyProvider(mockConfig$); - await esSearch.search({ params }); + await esSearch.search((mockContext as unknown) as RequestHandlerContext, { params }); expect(mockApiCaller).toBeCalled(); expect(mockApiCaller.mock.calls[0][0]).toBe('transport.request'); @@ -79,16 +66,9 @@ describe('ES search strategy', () => { mockApiCaller.mockResolvedValueOnce(mockAsyncResponse); const params = { index: 'logstash-*', body: { query: {} } }; - const esSearch = enhancedEsSearchStrategyProvider( - { - core: mockCoreSetup, - config$: mockConfig$, - }, - mockApiCaller, - mockSearch - ); + const esSearch = await enhancedEsSearchStrategyProvider(mockConfig$); - await esSearch.search({ id: 'foo', params }); + await esSearch.search((mockContext as unknown) as RequestHandlerContext, { id: 'foo', params }); expect(mockApiCaller).toBeCalled(); expect(mockApiCaller.mock.calls[0][0]).toBe('transport.request'); @@ -102,16 +82,9 @@ describe('ES search strategy', () => { mockApiCaller.mockResolvedValueOnce(mockAsyncResponse); const params = { index: 'foo-程', body: {} }; - const esSearch = enhancedEsSearchStrategyProvider( - { - core: mockCoreSetup, - config$: mockConfig$, - }, - mockApiCaller, - mockSearch - ); + const esSearch = await enhancedEsSearchStrategyProvider(mockConfig$); - await esSearch.search({ params }); + await esSearch.search((mockContext as unknown) as RequestHandlerContext, { params }); expect(mockApiCaller).toBeCalled(); expect(mockApiCaller.mock.calls[0][0]).toBe('transport.request'); @@ -124,16 +97,12 @@ describe('ES search strategy', () => { mockApiCaller.mockResolvedValueOnce(mockRollupResponse); const params = { index: 'foo-程', body: {} }; - const esSearch = enhancedEsSearchStrategyProvider( - { - core: mockCoreSetup, - config$: mockConfig$, - }, - mockApiCaller, - mockSearch - ); - - await esSearch.search({ indexType: 'rollup', params }); + const esSearch = await enhancedEsSearchStrategyProvider(mockConfig$); + + await esSearch.search((mockContext as unknown) as RequestHandlerContext, { + indexType: 'rollup', + params, + }); expect(mockApiCaller).toBeCalled(); expect(mockApiCaller.mock.calls[0][0]).toBe('transport.request'); diff --git a/x-pack/plugins/data_enhanced/server/search/es_search_strategy.ts b/x-pack/plugins/data_enhanced/server/search/es_search_strategy.ts index 15f2ca10af7f71..9083ab24a4521a 100644 --- a/x-pack/plugins/data_enhanced/server/search/es_search_strategy.ts +++ b/x-pack/plugins/data_enhanced/server/search/es_search_strategy.ts @@ -7,16 +7,16 @@ import { first } from 'rxjs/operators'; import { mapKeys, snakeCase } from 'lodash'; import { SearchResponse } from 'elasticsearch'; -import { APICaller } from '../../../../../src/core/server'; +import { Observable } from 'rxjs'; +import { APICaller, SharedGlobalConfig } from '../../../../../src/core/server'; import { ES_SEARCH_STRATEGY } from '../../../../../src/plugins/data/common'; import { - ISearchContext, - TSearchStrategyProvider, ISearch, ISearchOptions, ISearchCancel, getDefaultSearchParams, getTotalLoaded, + ISearchStrategy, } from '../../../../../src/plugins/data/server'; import { IEnhancedEsSearchRequest } from '../../common'; import { shimHitsTotal } from './shim_hits_total'; @@ -28,15 +28,16 @@ export interface AsyncSearchResponse { response: SearchResponse; } -export const enhancedEsSearchStrategyProvider: TSearchStrategyProvider = ( - context: ISearchContext, - caller: APICaller -) => { +export const enhancedEsSearchStrategyProvider = ( + config$: Observable +): ISearchStrategy => { const search: ISearch = async ( + context, request: IEnhancedEsSearchRequest, options ) => { - const config = await context.config$.pipe(first()).toPromise(); + const config = await config$.pipe(first()).toPromise(); + const caller = context.core.elasticsearch.legacy.client.callAsCurrentUser; const defaultParams = getDefaultSearchParams(config); const params = { ...defaultParams, ...request.params }; @@ -45,10 +46,13 @@ export const enhancedEsSearchStrategyProvider: TSearchStrategyProvider = async (id) => { + const cancel: ISearchCancel = async (context, id) => { const method = 'DELETE'; const path = encodeURI(`/_async_search/${id}`); - await caller('transport.request', { method, path }); + await context.core.elasticsearch.legacy.client.callAsCurrentUser('transport.request', { + method, + path, + }); }; return { search, cancel }; diff --git a/x-pack/plugins/reporting/public/plugin.tsx b/x-pack/plugins/reporting/public/plugin.tsx index fcaa295a45eccd..aad3d9b026c6ee 100644 --- a/x-pack/plugins/reporting/public/plugin.tsx +++ b/x-pack/plugins/reporting/public/plugin.tsx @@ -155,8 +155,6 @@ export class ReportingPublicPlugin implements Plugin { ); } - // FIXME: only perform these actions for authenticated routes - // Depends on https://github.com/elastic/kibana/pull/39477 public start(core: CoreStart) { const { http, notifications } = core; const apiClient = new ReportingAPIClient(http); diff --git a/x-pack/plugins/reporting/server/export_types/common/execute_job/decrypt_job_headers.ts b/x-pack/plugins/reporting/server/export_types/common/execute_job/decrypt_job_headers.ts index e5124c80601d7f..579b5196ad4d96 100644 --- a/x-pack/plugins/reporting/server/export_types/common/execute_job/decrypt_job_headers.ts +++ b/x-pack/plugins/reporting/server/export_types/common/execute_job/decrypt_job_headers.ts @@ -14,14 +14,14 @@ interface HasEncryptedHeaders { // TODO merge functionality with CSV execute job export const decryptJobHeaders = async < JobParamsType, - JobDocPayloadType extends HasEncryptedHeaders + ScheduledTaskParamsType extends HasEncryptedHeaders >({ encryptionKey, job, logger, }: { encryptionKey?: string; - job: JobDocPayloadType; + job: ScheduledTaskParamsType; logger: LevelLogger; }): Promise> => { try { diff --git a/x-pack/plugins/reporting/server/export_types/common/execute_job/get_conditional_headers.test.ts b/x-pack/plugins/reporting/server/export_types/common/execute_job/get_conditional_headers.test.ts index 5d651ad5f8aea1..030ced5dc4b80b 100644 --- a/x-pack/plugins/reporting/server/export_types/common/execute_job/get_conditional_headers.test.ts +++ b/x-pack/plugins/reporting/server/export_types/common/execute_job/get_conditional_headers.test.ts @@ -8,8 +8,8 @@ import sinon from 'sinon'; import { ReportingConfig } from '../../../'; import { ReportingCore } from '../../../core'; import { createMockReportingCore } from '../../../test_helpers'; -import { JobDocPayload } from '../../../types'; -import { JobDocPayloadPDF } from '../../printable_pdf/types'; +import { ScheduledTaskParams } from '../../../types'; +import { ScheduledTaskParamsPDF } from '../../printable_pdf/types'; import { getConditionalHeaders, getCustomLogo } from './index'; let mockConfig: ReportingConfig; @@ -37,7 +37,7 @@ describe('conditions', () => { }; const conditionalHeaders = await getConditionalHeaders({ - job: {} as JobDocPayload, + job: {} as ScheduledTaskParams, filteredHeaders: permittedHeaders, config: mockConfig, }); @@ -64,14 +64,14 @@ test('uses basePath from job when creating saved object service', async () => { baz: 'quix', }; const conditionalHeaders = await getConditionalHeaders({ - job: {} as JobDocPayload, + job: {} as ScheduledTaskParams, filteredHeaders: permittedHeaders, config: mockConfig, }); const jobBasePath = '/sbp/s/marketing'; await getCustomLogo({ reporting: mockReportingPlugin, - job: { basePath: jobBasePath } as JobDocPayloadPDF, + job: { basePath: jobBasePath } as ScheduledTaskParamsPDF, conditionalHeaders, config: mockConfig, }); @@ -94,14 +94,14 @@ test(`uses basePath from server if job doesn't have a basePath when creating sav baz: 'quix', }; const conditionalHeaders = await getConditionalHeaders({ - job: {} as JobDocPayload, + job: {} as ScheduledTaskParams, filteredHeaders: permittedHeaders, config: mockConfig, }); await getCustomLogo({ reporting: mockReportingPlugin, - job: {} as JobDocPayloadPDF, + job: {} as ScheduledTaskParamsPDF, conditionalHeaders, config: mockConfig, }); @@ -139,7 +139,7 @@ describe('config formatting', () => { mockConfig = getMockConfig(mockConfigGet); const conditionalHeaders = await getConditionalHeaders({ - job: {} as JobDocPayload, + job: {} as ScheduledTaskParams, filteredHeaders: {}, config: mockConfig, }); diff --git a/x-pack/plugins/reporting/server/export_types/common/execute_job/get_conditional_headers.ts b/x-pack/plugins/reporting/server/export_types/common/execute_job/get_conditional_headers.ts index 6854f678aa9759..7a50eaac80d859 100644 --- a/x-pack/plugins/reporting/server/export_types/common/execute_job/get_conditional_headers.ts +++ b/x-pack/plugins/reporting/server/export_types/common/execute_job/get_conditional_headers.ts @@ -7,13 +7,13 @@ import { ReportingConfig } from '../../../'; import { ConditionalHeaders } from '../../../types'; -export const getConditionalHeaders = ({ +export const getConditionalHeaders = ({ config, job, filteredHeaders, }: { config: ReportingConfig; - job: JobDocPayloadType; + job: ScheduledTaskParamsType; filteredHeaders: Record; }) => { const { kbnConfig } = config; diff --git a/x-pack/plugins/reporting/server/export_types/common/execute_job/get_custom_logo.test.ts b/x-pack/plugins/reporting/server/export_types/common/execute_job/get_custom_logo.test.ts index bd6eb4644d87f9..c364752c8dd0f5 100644 --- a/x-pack/plugins/reporting/server/export_types/common/execute_job/get_custom_logo.test.ts +++ b/x-pack/plugins/reporting/server/export_types/common/execute_job/get_custom_logo.test.ts @@ -6,7 +6,7 @@ import { ReportingCore } from '../../../core'; import { createMockReportingCore } from '../../../test_helpers'; -import { JobDocPayloadPDF } from '../../printable_pdf/types'; +import { ScheduledTaskParamsPDF } from '../../printable_pdf/types'; import { getConditionalHeaders, getCustomLogo } from './index'; const mockConfigGet = jest.fn().mockImplementation((key: string) => { @@ -37,7 +37,7 @@ test(`gets logo from uiSettings`, async () => { }); const conditionalHeaders = await getConditionalHeaders({ - job: {} as JobDocPayloadPDF, + job: {} as ScheduledTaskParamsPDF, filteredHeaders: permittedHeaders, config: mockConfig, }); @@ -45,7 +45,7 @@ test(`gets logo from uiSettings`, async () => { const { logo } = await getCustomLogo({ reporting: mockReportingPlugin, config: mockConfig, - job: {} as JobDocPayloadPDF, + job: {} as ScheduledTaskParamsPDF, conditionalHeaders, }); diff --git a/x-pack/plugins/reporting/server/export_types/common/execute_job/get_custom_logo.ts b/x-pack/plugins/reporting/server/export_types/common/execute_job/get_custom_logo.ts index 85d1272fc22ced..36c02eb47565c5 100644 --- a/x-pack/plugins/reporting/server/export_types/common/execute_job/get_custom_logo.ts +++ b/x-pack/plugins/reporting/server/export_types/common/execute_job/get_custom_logo.ts @@ -7,7 +7,7 @@ import { ReportingConfig, ReportingCore } from '../../../'; import { UI_SETTINGS_CUSTOM_PDF_LOGO } from '../../../../common/constants'; import { ConditionalHeaders } from '../../../types'; -import { JobDocPayloadPDF } from '../../printable_pdf/types'; // Logo is PDF only +import { ScheduledTaskParamsPDF } from '../../printable_pdf/types'; // Logo is PDF only export const getCustomLogo = async ({ reporting, @@ -17,7 +17,7 @@ export const getCustomLogo = async ({ }: { reporting: ReportingCore; config: ReportingConfig; - job: JobDocPayloadPDF; + job: ScheduledTaskParamsPDF; conditionalHeaders: ConditionalHeaders; }) => { const serverBasePath: string = config.kbnConfig.get('server', 'basePath'); diff --git a/x-pack/plugins/reporting/server/export_types/common/execute_job/get_full_urls.test.ts b/x-pack/plugins/reporting/server/export_types/common/execute_job/get_full_urls.test.ts index cacea41477ea4a..ad952c084d4f39 100644 --- a/x-pack/plugins/reporting/server/export_types/common/execute_job/get_full_urls.test.ts +++ b/x-pack/plugins/reporting/server/export_types/common/execute_job/get_full_urls.test.ts @@ -5,12 +5,12 @@ */ import { ReportingConfig } from '../../../'; -import { JobDocPayloadPNG } from '../../png/types'; -import { JobDocPayloadPDF } from '../../printable_pdf/types'; +import { ScheduledTaskParamsPNG } from '../../png/types'; +import { ScheduledTaskParamsPDF } from '../../printable_pdf/types'; import { getFullUrls } from './get_full_urls'; interface FullUrlsOpts { - job: JobDocPayloadPNG & JobDocPayloadPDF; + job: ScheduledTaskParamsPNG & ScheduledTaskParamsPDF; config: ReportingConfig; } @@ -35,7 +35,7 @@ beforeEach(() => { mockConfig = getMockConfig(mockConfigGet); }); -const getMockJob = (base: object) => base as JobDocPayloadPNG & JobDocPayloadPDF; +const getMockJob = (base: object) => base as ScheduledTaskParamsPNG & ScheduledTaskParamsPDF; test(`fails if no URL is passed`, async () => { const fn = () => getFullUrls({ job: getMockJob({}), config: mockConfig } as FullUrlsOpts); diff --git a/x-pack/plugins/reporting/server/export_types/common/execute_job/get_full_urls.ts b/x-pack/plugins/reporting/server/export_types/common/execute_job/get_full_urls.ts index bcd7f122748cb8..67bc8d16fa758d 100644 --- a/x-pack/plugins/reporting/server/export_types/common/execute_job/get_full_urls.ts +++ b/x-pack/plugins/reporting/server/export_types/common/execute_job/get_full_urls.ts @@ -13,22 +13,26 @@ import { import { ReportingConfig } from '../../..'; import { getAbsoluteUrlFactory } from '../../../../common/get_absolute_url'; import { validateUrls } from '../../../../common/validate_urls'; -import { JobDocPayloadPNG } from '../../png/types'; -import { JobDocPayloadPDF } from '../../printable_pdf/types'; +import { ScheduledTaskParamsPNG } from '../../png/types'; +import { ScheduledTaskParamsPDF } from '../../printable_pdf/types'; -function isPngJob(job: JobDocPayloadPNG | JobDocPayloadPDF): job is JobDocPayloadPNG { - return (job as JobDocPayloadPNG).relativeUrl !== undefined; +function isPngJob( + job: ScheduledTaskParamsPNG | ScheduledTaskParamsPDF +): job is ScheduledTaskParamsPNG { + return (job as ScheduledTaskParamsPNG).relativeUrl !== undefined; } -function isPdfJob(job: JobDocPayloadPNG | JobDocPayloadPDF): job is JobDocPayloadPDF { - return (job as JobDocPayloadPDF).relativeUrls !== undefined; +function isPdfJob( + job: ScheduledTaskParamsPNG | ScheduledTaskParamsPDF +): job is ScheduledTaskParamsPDF { + return (job as ScheduledTaskParamsPDF).relativeUrls !== undefined; } -export function getFullUrls({ +export function getFullUrls({ config, job, }: { config: ReportingConfig; - job: JobDocPayloadPDF | JobDocPayloadPNG; + job: ScheduledTaskParamsPDF | ScheduledTaskParamsPNG; }) { const [basePath, protocol, hostname, port] = [ config.kbnConfig.get('server', 'basePath'), diff --git a/x-pack/plugins/reporting/server/export_types/common/execute_job/omit_blacklisted_headers.ts b/x-pack/plugins/reporting/server/export_types/common/execute_job/omit_blacklisted_headers.ts index 5147881a980eac..db7137c30513bf 100644 --- a/x-pack/plugins/reporting/server/export_types/common/execute_job/omit_blacklisted_headers.ts +++ b/x-pack/plugins/reporting/server/export_types/common/execute_job/omit_blacklisted_headers.ts @@ -9,11 +9,11 @@ import { KBN_SCREENSHOT_HEADER_BLACKLIST_STARTS_WITH_PATTERN, } from '../../../../common/constants'; -export const omitBlacklistedHeaders = ({ +export const omitBlacklistedHeaders = ({ job, decryptedHeaders, }: { - job: JobDocPayloadType; + job: ScheduledTaskParamsType; decryptedHeaders: Record; }) => { const filteredHeaders: Record = omit( diff --git a/x-pack/plugins/reporting/server/export_types/csv/index.ts b/x-pack/plugins/reporting/server/export_types/csv/index.ts index 8642a6d5758a80..b5eacdfc62c8b9 100644 --- a/x-pack/plugins/reporting/server/export_types/csv/index.ts +++ b/x-pack/plugins/reporting/server/export_types/csv/index.ts @@ -15,21 +15,21 @@ import { import { CSV_JOB_TYPE as jobType } from '../../../constants'; import { ESQueueCreateJobFn, ESQueueWorkerExecuteFn, ExportTypeDefinition } from '../../types'; import { metadata } from './metadata'; -import { createJobFactory } from './server/create_job'; -import { executeJobFactory } from './server/execute_job'; -import { JobDocPayloadDiscoverCsv, JobParamsDiscoverCsv } from './types'; +import { scheduleTaskFnFactory } from './server/create_job'; +import { runTaskFnFactory } from './server/execute_job'; +import { JobParamsDiscoverCsv, ScheduledTaskParamsCSV } from './types'; export const getExportType = (): ExportTypeDefinition< JobParamsDiscoverCsv, ESQueueCreateJobFn, - JobDocPayloadDiscoverCsv, - ESQueueWorkerExecuteFn + ScheduledTaskParamsCSV, + ESQueueWorkerExecuteFn > => ({ ...metadata, jobType, jobContentExtension: 'csv', - createJobFactory, - executeJobFactory, + scheduleTaskFnFactory, + runTaskFnFactory, validLicenses: [ LICENSE_TYPE_TRIAL, LICENSE_TYPE_BASIC, diff --git a/x-pack/plugins/reporting/server/export_types/csv/server/create_job.ts b/x-pack/plugins/reporting/server/export_types/csv/server/create_job.ts index acf7f0505a735e..c4fa1cd8e4fa6e 100644 --- a/x-pack/plugins/reporting/server/export_types/csv/server/create_job.ts +++ b/x-pack/plugins/reporting/server/export_types/csv/server/create_job.ts @@ -4,24 +4,18 @@ * you may not use this file except in compliance with the Elastic License. */ -import { KibanaRequest, RequestHandlerContext } from 'src/core/server'; -import { ReportingCore } from '../../../'; import { cryptoFactory } from '../../../lib'; -import { CreateJobFactory, ESQueueCreateJobFn } from '../../../types'; +import { ESQueueCreateJobFn, ScheduleTaskFnFactory } from '../../../types'; import { JobParamsDiscoverCsv } from '../types'; -export const createJobFactory: CreateJobFactory> = function createJobFactoryFn(reporting: ReportingCore) { +>> = function createJobFactoryFn(reporting) { const config = reporting.getConfig(); const crypto = cryptoFactory(config.get('encryptionKey')); const setupDeps = reporting.getPluginSetupDeps(); - return async function createJob( - jobParams: JobParamsDiscoverCsv, - context: RequestHandlerContext, - request: KibanaRequest - ) { + return async function scheduleTask(jobParams, context, request) { const serializedEncryptedHeaders = await crypto.encrypt(request.headers); const savedObjectsClient = context.core.savedObjects.client; diff --git a/x-pack/plugins/reporting/server/export_types/csv/server/execute_job.test.ts b/x-pack/plugins/reporting/server/export_types/csv/server/execute_job.test.ts index 4ce448e953bd17..d1297454971c79 100644 --- a/x-pack/plugins/reporting/server/export_types/csv/server/execute_job.test.ts +++ b/x-pack/plugins/reporting/server/export_types/csv/server/execute_job.test.ts @@ -20,8 +20,8 @@ import { CSV_BOM_CHARS } from '../../../../common/constants'; import { LevelLogger } from '../../../lib'; import { setFieldFormats } from '../../../services'; import { createMockReportingCore } from '../../../test_helpers'; -import { JobDocPayloadDiscoverCsv } from '../types'; -import { executeJobFactory } from './execute_job'; +import { ScheduledTaskParamsCSV } from '../types'; +import { runTaskFnFactory } from './execute_job'; const delay = (ms: number) => new Promise((resolve) => setTimeout(() => resolve(), ms)); @@ -30,7 +30,7 @@ const getRandomScrollId = () => { return puid.generate(); }; -const getJobDocPayload = (baseObj: any) => baseObj as JobDocPayloadDiscoverCsv; +const getScheduledTaskParams = (baseObj: any) => baseObj as ScheduledTaskParamsCSV; describe('CSV Execute Job', function () { const encryptionKey = 'testEncryptionKey'; @@ -125,10 +125,10 @@ describe('CSV Execute Job', function () { describe('basic Elasticsearch call behavior', function () { it('should decrypt encrypted headers and pass to callAsCurrentUser', async function () { - const executeJob = await executeJobFactory(mockReportingCore, mockLogger); - await executeJob( + const runTask = await runTaskFnFactory(mockReportingCore, mockLogger); + await runTask( 'job456', - getJobDocPayload({ + getScheduledTaskParams({ headers: encryptedHeaders, fields: [], searchRequest: { index: null, body: null }, @@ -145,8 +145,8 @@ describe('CSV Execute Job', function () { testBody: true, }; - const executeJob = await executeJobFactory(mockReportingCore, mockLogger); - const job = getJobDocPayload({ + const runTask = await runTaskFnFactory(mockReportingCore, mockLogger); + const job = getScheduledTaskParams({ headers: encryptedHeaders, fields: [], searchRequest: { @@ -155,7 +155,7 @@ describe('CSV Execute Job', function () { }, }); - await executeJob('job777', job, cancellationToken); + await runTask('job777', job, cancellationToken); const searchCall = callAsCurrentUserStub.firstCall; expect(searchCall.args[0]).toBe('search'); @@ -172,10 +172,10 @@ describe('CSV Execute Job', function () { _scroll_id: scrollId, }); callAsCurrentUserStub.onSecondCall().resolves(defaultElasticsearchResponse); - const executeJob = await executeJobFactory(mockReportingCore, mockLogger); - await executeJob( + const runTask = await runTaskFnFactory(mockReportingCore, mockLogger); + await runTask( 'job456', - getJobDocPayload({ + getScheduledTaskParams({ headers: encryptedHeaders, fields: [], searchRequest: { index: null, body: null }, @@ -190,10 +190,10 @@ describe('CSV Execute Job', function () { }); it('should not execute scroll if there are no hits from the search', async function () { - const executeJob = await executeJobFactory(mockReportingCore, mockLogger); - await executeJob( + const runTask = await runTaskFnFactory(mockReportingCore, mockLogger); + await runTask( 'job456', - getJobDocPayload({ + getScheduledTaskParams({ headers: encryptedHeaders, fields: [], searchRequest: { index: null, body: null }, @@ -224,10 +224,10 @@ describe('CSV Execute Job', function () { _scroll_id: 'scrollId', }); - const executeJob = await executeJobFactory(mockReportingCore, mockLogger); - await executeJob( + const runTask = await runTaskFnFactory(mockReportingCore, mockLogger); + await runTask( 'job456', - getJobDocPayload({ + getScheduledTaskParams({ headers: encryptedHeaders, fields: [], searchRequest: { index: null, body: null }, @@ -263,10 +263,10 @@ describe('CSV Execute Job', function () { _scroll_id: lastScrollId, }); - const executeJob = await executeJobFactory(mockReportingCore, mockLogger); - await executeJob( + const runTask = await runTaskFnFactory(mockReportingCore, mockLogger); + await runTask( 'job456', - getJobDocPayload({ + getScheduledTaskParams({ headers: encryptedHeaders, fields: [], searchRequest: { index: null, body: null }, @@ -295,16 +295,16 @@ describe('CSV Execute Job', function () { _scroll_id: lastScrollId, }); - const executeJob = await executeJobFactory(mockReportingCore, mockLogger); - const jobParams = getJobDocPayload({ + const runTask = await runTaskFnFactory(mockReportingCore, mockLogger); + const jobParams = getScheduledTaskParams({ headers: encryptedHeaders, fields: ['one', 'two'], conflictedTypesFields: undefined, searchRequest: { index: null, body: null }, }); - await expect( - executeJob('job123', jobParams, cancellationToken) - ).rejects.toMatchInlineSnapshot(`[TypeError: Cannot read property 'indexOf' of undefined]`); + await expect(runTask('job123', jobParams, cancellationToken)).rejects.toMatchInlineSnapshot( + `[TypeError: Cannot read property 'indexOf' of undefined]` + ); const lastCall = callAsCurrentUserStub.getCall(callAsCurrentUserStub.callCount - 1); expect(lastCall.args[0]).toBe('clearScroll'); @@ -322,14 +322,14 @@ describe('CSV Execute Job', function () { _scroll_id: 'scrollId', }); - const executeJob = await executeJobFactory(mockReportingCore, mockLogger); - const jobParams = getJobDocPayload({ + const runTask = await runTaskFnFactory(mockReportingCore, mockLogger); + const jobParams = getScheduledTaskParams({ headers: encryptedHeaders, fields: ['one', 'two'], conflictedTypesFields: [], searchRequest: { index: null, body: null }, }); - const { csv_contains_formulas: csvContainsFormulas } = await executeJob( + const { csv_contains_formulas: csvContainsFormulas } = await runTask( 'job123', jobParams, cancellationToken @@ -347,14 +347,14 @@ describe('CSV Execute Job', function () { _scroll_id: 'scrollId', }); - const executeJob = await executeJobFactory(mockReportingCore, mockLogger); - const jobParams = getJobDocPayload({ + const runTask = await runTaskFnFactory(mockReportingCore, mockLogger); + const jobParams = getScheduledTaskParams({ headers: encryptedHeaders, fields: ['=SUM(A1:A2)', 'two'], conflictedTypesFields: [], searchRequest: { index: null, body: null }, }); - const { csv_contains_formulas: csvContainsFormulas } = await executeJob( + const { csv_contains_formulas: csvContainsFormulas } = await runTask( 'job123', jobParams, cancellationToken @@ -373,14 +373,14 @@ describe('CSV Execute Job', function () { _scroll_id: 'scrollId', }); - const executeJob = await executeJobFactory(mockReportingCore, mockLogger); - const jobParams = getJobDocPayload({ + const runTask = await runTaskFnFactory(mockReportingCore, mockLogger); + const jobParams = getScheduledTaskParams({ headers: encryptedHeaders, fields: ['one', 'two'], conflictedTypesFields: [], searchRequest: { index: null, body: null }, }); - const { csv_contains_formulas: csvContainsFormulas } = await executeJob( + const { csv_contains_formulas: csvContainsFormulas } = await runTask( 'job123', jobParams, cancellationToken @@ -399,15 +399,15 @@ describe('CSV Execute Job', function () { _scroll_id: 'scrollId', }); - const executeJob = await executeJobFactory(mockReportingCore, mockLogger); - const jobParams = getJobDocPayload({ + const runTask = await runTaskFnFactory(mockReportingCore, mockLogger); + const jobParams = getScheduledTaskParams({ headers: encryptedHeaders, fields: ['=SUM(A1:A2)', 'two'], conflictedTypesFields: [], searchRequest: { index: null, body: null }, }); - const { csv_contains_formulas: csvContainsFormulas } = await executeJob( + const { csv_contains_formulas: csvContainsFormulas } = await runTask( 'job123', jobParams, cancellationToken @@ -425,14 +425,14 @@ describe('CSV Execute Job', function () { _scroll_id: 'scrollId', }); - const executeJob = await executeJobFactory(mockReportingCore, mockLogger); - const jobParams = getJobDocPayload({ + const runTask = await runTaskFnFactory(mockReportingCore, mockLogger); + const jobParams = getScheduledTaskParams({ headers: encryptedHeaders, fields: ['one', 'two'], conflictedTypesFields: [], searchRequest: { index: null, body: null }, }); - const { csv_contains_formulas: csvContainsFormulas } = await executeJob( + const { csv_contains_formulas: csvContainsFormulas } = await runTask( 'job123', jobParams, cancellationToken @@ -452,14 +452,14 @@ describe('CSV Execute Job', function () { _scroll_id: 'scrollId', }); - const executeJob = await executeJobFactory(mockReportingCore, mockLogger); - const jobParams = getJobDocPayload({ + const runTask = await runTaskFnFactory(mockReportingCore, mockLogger); + const jobParams = getScheduledTaskParams({ headers: encryptedHeaders, fields: ['one', 'two'], conflictedTypesFields: [], searchRequest: { index: null, body: null }, }); - const { content } = await executeJob('job123', jobParams, cancellationToken); + const { content } = await runTask('job123', jobParams, cancellationToken); expect(content).toEqual(`${CSV_BOM_CHARS}one,two\none,bar\n`); }); @@ -473,14 +473,14 @@ describe('CSV Execute Job', function () { _scroll_id: 'scrollId', }); - const executeJob = await executeJobFactory(mockReportingCore, mockLogger); - const jobParams = getJobDocPayload({ + const runTask = await runTaskFnFactory(mockReportingCore, mockLogger); + const jobParams = getScheduledTaskParams({ headers: encryptedHeaders, fields: ['one', 'two'], conflictedTypesFields: [], searchRequest: { index: null, body: null }, }); - const { content } = await executeJob('job123', jobParams, cancellationToken); + const { content } = await runTask('job123', jobParams, cancellationToken); expect(content).toEqual('one,two\none,bar\n'); }); @@ -496,14 +496,14 @@ describe('CSV Execute Job', function () { _scroll_id: 'scrollId', }); - const executeJob = await executeJobFactory(mockReportingCore, mockLogger); - const jobParams = getJobDocPayload({ + const runTask = await runTaskFnFactory(mockReportingCore, mockLogger); + const jobParams = getScheduledTaskParams({ headers: encryptedHeaders, fields: ['one', 'two'], conflictedTypesFields: [], searchRequest: { index: null, body: null }, }); - const { content } = await executeJob('job123', jobParams, cancellationToken); + const { content } = await runTask('job123', jobParams, cancellationToken); expect(content).toEqual("one,two\n\"'=cmd|' /C calc'!A0\",bar\n"); }); @@ -517,14 +517,14 @@ describe('CSV Execute Job', function () { _scroll_id: 'scrollId', }); - const executeJob = await executeJobFactory(mockReportingCore, mockLogger); - const jobParams = getJobDocPayload({ + const runTask = await runTaskFnFactory(mockReportingCore, mockLogger); + const jobParams = getScheduledTaskParams({ headers: encryptedHeaders, fields: ['one', 'two'], conflictedTypesFields: [], searchRequest: { index: null, body: null }, }); - const { content } = await executeJob('job123', jobParams, cancellationToken); + const { content } = await runTask('job123', jobParams, cancellationToken); expect(content).toEqual('one,two\n"=cmd|\' /C calc\'!A0",bar\n'); }); @@ -533,15 +533,15 @@ describe('CSV Execute Job', function () { describe('Elasticsearch call errors', function () { it('should reject Promise if search call errors out', async function () { callAsCurrentUserStub.rejects(new Error()); - const executeJob = await executeJobFactory(mockReportingCore, mockLogger); - const jobParams = getJobDocPayload({ + const runTask = await runTaskFnFactory(mockReportingCore, mockLogger); + const jobParams = getScheduledTaskParams({ headers: encryptedHeaders, fields: [], searchRequest: { index: null, body: null }, }); - await expect( - executeJob('job123', jobParams, cancellationToken) - ).rejects.toMatchInlineSnapshot(`[Error]`); + await expect(runTask('job123', jobParams, cancellationToken)).rejects.toMatchInlineSnapshot( + `[Error]` + ); }); it('should reject Promise if scroll call errors out', async function () { @@ -552,15 +552,15 @@ describe('CSV Execute Job', function () { _scroll_id: 'scrollId', }); callAsCurrentUserStub.onSecondCall().rejects(new Error()); - const executeJob = await executeJobFactory(mockReportingCore, mockLogger); - const jobParams = getJobDocPayload({ + const runTask = await runTaskFnFactory(mockReportingCore, mockLogger); + const jobParams = getScheduledTaskParams({ headers: encryptedHeaders, fields: [], searchRequest: { index: null, body: null }, }); - await expect( - executeJob('job123', jobParams, cancellationToken) - ).rejects.toMatchInlineSnapshot(`[Error]`); + await expect(runTask('job123', jobParams, cancellationToken)).rejects.toMatchInlineSnapshot( + `[Error]` + ); }); }); @@ -573,15 +573,13 @@ describe('CSV Execute Job', function () { _scroll_id: undefined, }); - const executeJob = await executeJobFactory(mockReportingCore, mockLogger); - const jobParams = getJobDocPayload({ + const runTask = await runTaskFnFactory(mockReportingCore, mockLogger); + const jobParams = getScheduledTaskParams({ headers: encryptedHeaders, fields: [], searchRequest: { index: null, body: null }, }); - await expect( - executeJob('job123', jobParams, cancellationToken) - ).rejects.toMatchInlineSnapshot( + await expect(runTask('job123', jobParams, cancellationToken)).rejects.toMatchInlineSnapshot( `[Error: Expected _scroll_id in the following Elasticsearch response: {"hits":{"hits":[{}]}}]` ); }); @@ -594,15 +592,13 @@ describe('CSV Execute Job', function () { _scroll_id: undefined, }); - const executeJob = await executeJobFactory(mockReportingCore, mockLogger); - const jobParams = getJobDocPayload({ + const runTask = await runTaskFnFactory(mockReportingCore, mockLogger); + const jobParams = getScheduledTaskParams({ headers: encryptedHeaders, fields: [], searchRequest: { index: null, body: null }, }); - await expect( - executeJob('job123', jobParams, cancellationToken) - ).rejects.toMatchInlineSnapshot( + await expect(runTask('job123', jobParams, cancellationToken)).rejects.toMatchInlineSnapshot( `[Error: Expected _scroll_id in the following Elasticsearch response: {"hits":{"hits":[]}}]` ); }); @@ -622,15 +618,13 @@ describe('CSV Execute Job', function () { _scroll_id: undefined, }); - const executeJob = await executeJobFactory(mockReportingCore, mockLogger); - const jobParams = getJobDocPayload({ + const runTask = await runTaskFnFactory(mockReportingCore, mockLogger); + const jobParams = getScheduledTaskParams({ headers: encryptedHeaders, fields: [], searchRequest: { index: null, body: null }, }); - await expect( - executeJob('job123', jobParams, cancellationToken) - ).rejects.toMatchInlineSnapshot( + await expect(runTask('job123', jobParams, cancellationToken)).rejects.toMatchInlineSnapshot( `[Error: Expected _scroll_id in the following Elasticsearch response: {"hits":{"hits":[{}]}}]` ); }); @@ -650,15 +644,13 @@ describe('CSV Execute Job', function () { _scroll_id: undefined, }); - const executeJob = await executeJobFactory(mockReportingCore, mockLogger); - const jobParams = getJobDocPayload({ + const runTask = await runTaskFnFactory(mockReportingCore, mockLogger); + const jobParams = getScheduledTaskParams({ headers: encryptedHeaders, fields: [], searchRequest: { index: null, body: null }, }); - await expect( - executeJob('job123', jobParams, cancellationToken) - ).rejects.toMatchInlineSnapshot( + await expect(runTask('job123', jobParams, cancellationToken)).rejects.toMatchInlineSnapshot( `[Error: Expected _scroll_id in the following Elasticsearch response: {"hits":{"hits":[]}}]` ); }); @@ -686,10 +678,10 @@ describe('CSV Execute Job', function () { }); it('should stop calling Elasticsearch when cancellationToken.cancel is called', async function () { - const executeJob = await executeJobFactory(mockReportingCore, mockLogger); - executeJob( + const runTask = await runTaskFnFactory(mockReportingCore, mockLogger); + runTask( 'job345', - getJobDocPayload({ + getScheduledTaskParams({ headers: encryptedHeaders, fields: [], searchRequest: { index: null, body: null }, @@ -705,10 +697,10 @@ describe('CSV Execute Job', function () { }); it(`shouldn't call clearScroll if it never got a scrollId`, async function () { - const executeJob = await executeJobFactory(mockReportingCore, mockLogger); - executeJob( + const runTask = await runTaskFnFactory(mockReportingCore, mockLogger); + runTask( 'job345', - getJobDocPayload({ + getScheduledTaskParams({ headers: encryptedHeaders, fields: [], searchRequest: { index: null, body: null }, @@ -723,10 +715,10 @@ describe('CSV Execute Job', function () { }); it('should call clearScroll if it got a scrollId', async function () { - const executeJob = await executeJobFactory(mockReportingCore, mockLogger); - executeJob( + const runTask = await runTaskFnFactory(mockReportingCore, mockLogger); + runTask( 'job345', - getJobDocPayload({ + getScheduledTaskParams({ headers: encryptedHeaders, fields: [], searchRequest: { index: null, body: null }, @@ -745,54 +737,54 @@ describe('CSV Execute Job', function () { describe('csv content', function () { it('should write column headers to output, even if there are no results', async function () { - const executeJob = await executeJobFactory(mockReportingCore, mockLogger); - const jobParams = getJobDocPayload({ + const runTask = await runTaskFnFactory(mockReportingCore, mockLogger); + const jobParams = getScheduledTaskParams({ headers: encryptedHeaders, fields: ['one', 'two'], searchRequest: { index: null, body: null }, }); - const { content } = await executeJob('job123', jobParams, cancellationToken); + const { content } = await runTask('job123', jobParams, cancellationToken); expect(content).toBe(`one,two\n`); }); it('should use custom uiSettings csv:separator for header', async function () { mockUiSettingsClient.get.withArgs(CSV_SEPARATOR_SETTING).returns(';'); - const executeJob = await executeJobFactory(mockReportingCore, mockLogger); - const jobParams = getJobDocPayload({ + const runTask = await runTaskFnFactory(mockReportingCore, mockLogger); + const jobParams = getScheduledTaskParams({ headers: encryptedHeaders, fields: ['one', 'two'], searchRequest: { index: null, body: null }, }); - const { content } = await executeJob('job123', jobParams, cancellationToken); + const { content } = await runTask('job123', jobParams, cancellationToken); expect(content).toBe(`one;two\n`); }); it('should escape column headers if uiSettings csv:quoteValues is true', async function () { mockUiSettingsClient.get.withArgs(CSV_QUOTE_VALUES_SETTING).returns(true); - const executeJob = await executeJobFactory(mockReportingCore, mockLogger); - const jobParams = getJobDocPayload({ + const runTask = await runTaskFnFactory(mockReportingCore, mockLogger); + const jobParams = getScheduledTaskParams({ headers: encryptedHeaders, fields: ['one and a half', 'two', 'three-and-four', 'five & six'], searchRequest: { index: null, body: null }, }); - const { content } = await executeJob('job123', jobParams, cancellationToken); + const { content } = await runTask('job123', jobParams, cancellationToken); expect(content).toBe(`"one and a half",two,"three-and-four","five & six"\n`); }); it(`shouldn't escape column headers if uiSettings csv:quoteValues is false`, async function () { mockUiSettingsClient.get.withArgs(CSV_QUOTE_VALUES_SETTING).returns(false); - const executeJob = await executeJobFactory(mockReportingCore, mockLogger); - const jobParams = getJobDocPayload({ + const runTask = await runTaskFnFactory(mockReportingCore, mockLogger); + const jobParams = getScheduledTaskParams({ headers: encryptedHeaders, fields: ['one and a half', 'two', 'three-and-four', 'five & six'], searchRequest: { index: null, body: null }, }); - const { content } = await executeJob('job123', jobParams, cancellationToken); + const { content } = await runTask('job123', jobParams, cancellationToken); expect(content).toBe(`one and a half,two,three-and-four,five & six\n`); }); it('should write column headers to output, when there are results', async function () { - const executeJob = await executeJobFactory(mockReportingCore, mockLogger); + const runTask = await runTaskFnFactory(mockReportingCore, mockLogger); callAsCurrentUserStub.onFirstCall().resolves({ hits: { hits: [{ one: '1', two: '2' }], @@ -800,19 +792,19 @@ describe('CSV Execute Job', function () { _scroll_id: 'scrollId', }); - const jobParams = getJobDocPayload({ + const jobParams = getScheduledTaskParams({ headers: encryptedHeaders, fields: ['one', 'two'], searchRequest: { index: null, body: null }, }); - const { content } = await executeJob('job123', jobParams, cancellationToken); + const { content } = await runTask('job123', jobParams, cancellationToken); const lines = content.split('\n'); const headerLine = lines[0]; expect(headerLine).toBe('one,two'); }); it('should use comma separated values of non-nested fields from _source', async function () { - const executeJob = await executeJobFactory(mockReportingCore, mockLogger); + const runTask = await runTaskFnFactory(mockReportingCore, mockLogger); callAsCurrentUserStub.onFirstCall().resolves({ hits: { hits: [{ _source: { one: 'foo', two: 'bar' } }], @@ -820,20 +812,20 @@ describe('CSV Execute Job', function () { _scroll_id: 'scrollId', }); - const jobParams = getJobDocPayload({ + const jobParams = getScheduledTaskParams({ headers: encryptedHeaders, fields: ['one', 'two'], conflictedTypesFields: [], searchRequest: { index: null, body: null }, }); - const { content } = await executeJob('job123', jobParams, cancellationToken); + const { content } = await runTask('job123', jobParams, cancellationToken); const lines = content.split('\n'); const valuesLine = lines[1]; expect(valuesLine).toBe('foo,bar'); }); it('should concatenate the hits from multiple responses', async function () { - const executeJob = await executeJobFactory(mockReportingCore, mockLogger); + const runTask = await runTaskFnFactory(mockReportingCore, mockLogger); callAsCurrentUserStub.onFirstCall().resolves({ hits: { hits: [{ _source: { one: 'foo', two: 'bar' } }], @@ -847,13 +839,13 @@ describe('CSV Execute Job', function () { _scroll_id: 'scrollId', }); - const jobParams = getJobDocPayload({ + const jobParams = getScheduledTaskParams({ headers: encryptedHeaders, fields: ['one', 'two'], conflictedTypesFields: [], searchRequest: { index: null, body: null }, }); - const { content } = await executeJob('job123', jobParams, cancellationToken); + const { content } = await runTask('job123', jobParams, cancellationToken); const lines = content.split('\n'); expect(lines[1]).toBe('foo,bar'); @@ -861,7 +853,7 @@ describe('CSV Execute Job', function () { }); it('should use field formatters to format fields', async function () { - const executeJob = await executeJobFactory(mockReportingCore, mockLogger); + const runTask = await runTaskFnFactory(mockReportingCore, mockLogger); callAsCurrentUserStub.onFirstCall().resolves({ hits: { hits: [{ _source: { one: 'foo', two: 'bar' } }], @@ -869,7 +861,7 @@ describe('CSV Execute Job', function () { _scroll_id: 'scrollId', }); - const jobParams = getJobDocPayload({ + const jobParams = getScheduledTaskParams({ headers: encryptedHeaders, fields: ['one', 'two'], conflictedTypesFields: [], @@ -884,7 +876,7 @@ describe('CSV Execute Job', function () { }, }, }); - const { content } = await executeJob('job123', jobParams, cancellationToken); + const { content } = await runTask('job123', jobParams, cancellationToken); const lines = content.split('\n'); expect(lines[1]).toBe('FOO,bar'); @@ -903,14 +895,14 @@ describe('CSV Execute Job', function () { beforeEach(async function () { configGetStub.withArgs('csv', 'maxSizeBytes').returns(1); - const executeJob = await executeJobFactory(mockReportingCore, mockLogger); - const jobParams = getJobDocPayload({ + const runTask = await runTaskFnFactory(mockReportingCore, mockLogger); + const jobParams = getScheduledTaskParams({ headers: encryptedHeaders, fields: ['one', 'two'], searchRequest: { index: null, body: null }, }); - ({ content, max_size_reached: maxSizeReached } = await executeJob( + ({ content, max_size_reached: maxSizeReached } = await runTask( 'job123', jobParams, cancellationToken @@ -933,14 +925,14 @@ describe('CSV Execute Job', function () { beforeEach(async function () { configGetStub.withArgs('csv', 'maxSizeBytes').returns(9); - const executeJob = await executeJobFactory(mockReportingCore, mockLogger); - const jobParams = getJobDocPayload({ + const runTask = await runTaskFnFactory(mockReportingCore, mockLogger); + const jobParams = getScheduledTaskParams({ headers: encryptedHeaders, fields: ['one', 'two'], searchRequest: { index: null, body: null }, }); - ({ content, max_size_reached: maxSizeReached } = await executeJob( + ({ content, max_size_reached: maxSizeReached } = await runTask( 'job123', jobParams, cancellationToken @@ -970,15 +962,15 @@ describe('CSV Execute Job', function () { _scroll_id: 'scrollId', }); - const executeJob = await executeJobFactory(mockReportingCore, mockLogger); - const jobParams = getJobDocPayload({ + const runTask = await runTaskFnFactory(mockReportingCore, mockLogger); + const jobParams = getScheduledTaskParams({ headers: encryptedHeaders, fields: ['one', 'two'], conflictedTypesFields: [], searchRequest: { index: null, body: null }, }); - ({ content, max_size_reached: maxSizeReached } = await executeJob( + ({ content, max_size_reached: maxSizeReached } = await runTask( 'job123', jobParams, cancellationToken @@ -1010,15 +1002,15 @@ describe('CSV Execute Job', function () { _scroll_id: 'scrollId', }); - const executeJob = await executeJobFactory(mockReportingCore, mockLogger); - const jobParams = getJobDocPayload({ + const runTask = await runTaskFnFactory(mockReportingCore, mockLogger); + const jobParams = getScheduledTaskParams({ headers: encryptedHeaders, fields: ['one', 'two'], conflictedTypesFields: [], searchRequest: { index: null, body: null }, }); - ({ content, max_size_reached: maxSizeReached } = await executeJob( + ({ content, max_size_reached: maxSizeReached } = await runTask( 'job123', jobParams, cancellationToken @@ -1047,15 +1039,15 @@ describe('CSV Execute Job', function () { _scroll_id: 'scrollId', }); - const executeJob = await executeJobFactory(mockReportingCore, mockLogger); - const jobParams = getJobDocPayload({ + const runTask = await runTaskFnFactory(mockReportingCore, mockLogger); + const jobParams = getScheduledTaskParams({ headers: encryptedHeaders, fields: ['one', 'two'], conflictedTypesFields: [], searchRequest: { index: null, body: null }, }); - await executeJob('job123', jobParams, cancellationToken); + await runTask('job123', jobParams, cancellationToken); const searchCall = callAsCurrentUserStub.firstCall; expect(searchCall.args[0]).toBe('search'); @@ -1073,15 +1065,15 @@ describe('CSV Execute Job', function () { _scroll_id: 'scrollId', }); - const executeJob = await executeJobFactory(mockReportingCore, mockLogger); - const jobParams = getJobDocPayload({ + const runTask = await runTaskFnFactory(mockReportingCore, mockLogger); + const jobParams = getScheduledTaskParams({ headers: encryptedHeaders, fields: ['one', 'two'], conflictedTypesFields: [], searchRequest: { index: null, body: null }, }); - await executeJob('job123', jobParams, cancellationToken); + await runTask('job123', jobParams, cancellationToken); const searchCall = callAsCurrentUserStub.firstCall; expect(searchCall.args[0]).toBe('search'); @@ -1099,15 +1091,15 @@ describe('CSV Execute Job', function () { _scroll_id: 'scrollId', }); - const executeJob = await executeJobFactory(mockReportingCore, mockLogger); - const jobParams = getJobDocPayload({ + const runTask = await runTaskFnFactory(mockReportingCore, mockLogger); + const jobParams = getScheduledTaskParams({ headers: encryptedHeaders, fields: ['one', 'two'], conflictedTypesFields: [], searchRequest: { index: null, body: null }, }); - await executeJob('job123', jobParams, cancellationToken); + await runTask('job123', jobParams, cancellationToken); const scrollCall = callAsCurrentUserStub.secondCall; expect(scrollCall.args[0]).toBe('scroll'); diff --git a/x-pack/plugins/reporting/server/export_types/csv/server/execute_job.ts b/x-pack/plugins/reporting/server/export_types/csv/server/execute_job.ts index 91a4db0469fb53..89fd014502f744 100644 --- a/x-pack/plugins/reporting/server/export_types/csv/server/execute_job.ts +++ b/x-pack/plugins/reporting/server/export_types/csv/server/execute_job.ts @@ -8,31 +8,26 @@ import { i18n } from '@kbn/i18n'; import Hapi from 'hapi'; import { IUiSettingsClient, KibanaRequest } from '../../../../../../../src/core/server'; import { - CSV_SEPARATOR_SETTING, CSV_QUOTE_VALUES_SETTING, + CSV_SEPARATOR_SETTING, } from '../../../../../../../src/plugins/share/server'; -import { ReportingCore } from '../../..'; import { CSV_BOM_CHARS, CSV_JOB_TYPE } from '../../../../common/constants'; import { getFieldFormats } from '../../../../server/services'; -import { cryptoFactory, LevelLogger } from '../../../lib'; -import { ESQueueWorkerExecuteFn, ExecuteJobFactory } from '../../../types'; -import { JobDocPayloadDiscoverCsv } from '../types'; +import { cryptoFactory } from '../../../lib'; +import { ESQueueWorkerExecuteFn, RunTaskFnFactory } from '../../../types'; +import { ScheduledTaskParamsCSV } from '../types'; import { fieldFormatMapFactory } from './lib/field_format_map'; import { createGenerateCsv } from './lib/generate_csv'; -export const executeJobFactory: ExecuteJobFactory> = async function executeJobFactoryFn(reporting: ReportingCore, parentLogger: LevelLogger) { +export const runTaskFnFactory: RunTaskFnFactory> = function executeJobFactoryFn(reporting, parentLogger) { const config = reporting.getConfig(); const crypto = cryptoFactory(config.get('encryptionKey')); const logger = parentLogger.clone([CSV_JOB_TYPE, 'execute-job']); const serverBasePath = config.kbnConfig.get('server', 'basePath'); - return async function executeJob( - jobId: string, - job: JobDocPayloadDiscoverCsv, - cancellationToken: any - ) { + return async function runTask(jobId, job, cancellationToken) { const elasticsearch = reporting.getElasticsearchService(); const jobLogger = logger.clone([jobId]); diff --git a/x-pack/plugins/reporting/server/export_types/csv/types.d.ts b/x-pack/plugins/reporting/server/export_types/csv/types.d.ts index c80cd5fd24fe5b..ab3e114c7c9952 100644 --- a/x-pack/plugins/reporting/server/export_types/csv/types.d.ts +++ b/x-pack/plugins/reporting/server/export_types/csv/types.d.ts @@ -5,7 +5,7 @@ */ import { CancellationToken } from '../../../common'; -import { JobParamPostPayload, JobDocPayload, ScrollConfig } from '../../types'; +import { JobParamPostPayload, ScheduledTaskParams, ScrollConfig } from '../../types'; export type RawValue = string | object | null | undefined; @@ -32,7 +32,7 @@ export interface JobParamsDiscoverCsv { post?: JobParamPostPayloadDiscoverCsv; } -export interface JobDocPayloadDiscoverCsv extends JobDocPayload { +export interface ScheduledTaskParamsCSV extends ScheduledTaskParams { basePath: string; searchRequest: any; fields: any; diff --git a/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/index.ts b/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/index.ts index 65802ee5bb7fbb..961a046c846e4d 100644 --- a/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/index.ts +++ b/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/index.ts @@ -15,16 +15,16 @@ import { import { CSV_FROM_SAVEDOBJECT_JOB_TYPE } from '../../../constants'; import { ExportTypeDefinition } from '../../types'; import { metadata } from './metadata'; -import { createJobFactory, ImmediateCreateJobFn } from './server/create_job'; -import { executeJobFactory, ImmediateExecuteFn } from './server/execute_job'; +import { ImmediateCreateJobFn, scheduleTaskFnFactory } from './server/create_job'; +import { ImmediateExecuteFn, runTaskFnFactory } from './server/execute_job'; import { JobParamsPanelCsv } from './types'; /* * These functions are exported to share with the API route handler that * generates csv from saved object immediately on request. */ -export { createJobFactory } from './server/create_job'; -export { executeJobFactory } from './server/execute_job'; +export { scheduleTaskFnFactory } from './server/create_job'; +export { runTaskFnFactory } from './server/execute_job'; export const getExportType = (): ExportTypeDefinition< JobParamsPanelCsv, @@ -35,8 +35,8 @@ export const getExportType = (): ExportTypeDefinition< ...metadata, jobType: CSV_FROM_SAVEDOBJECT_JOB_TYPE, jobContentExtension: 'csv', - createJobFactory, - executeJobFactory, + scheduleTaskFnFactory, + runTaskFnFactory, validLicenses: [ LICENSE_TYPE_TRIAL, LICENSE_TYPE_BASIC, diff --git a/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/server/create_job/create_job.ts b/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/server/create_job/create_job.ts deleted file mode 100644 index c187da5104d3f0..00000000000000 --- a/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/server/create_job/create_job.ts +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { notFound, notImplemented } from 'boom'; -import { get } from 'lodash'; -import { KibanaRequest, RequestHandlerContext } from 'src/core/server'; -import { ReportingCore } from '../../../..'; -import { CSV_FROM_SAVEDOBJECT_JOB_TYPE } from '../../../../../common/constants'; -import { cryptoFactory, LevelLogger } from '../../../../lib'; -import { CreateJobFactory, TimeRangeParams } from '../../../../types'; -import { - JobDocPayloadPanelCsv, - JobParamsPanelCsv, - SavedObject, - SavedObjectServiceError, - SavedSearchObjectAttributesJSON, - SearchPanel, - VisObjectAttributesJSON, -} from '../../types'; -import { createJobSearch } from './create_job_search'; - -export type ImmediateCreateJobFn = ( - jobParams: JobParamsType, - headers: KibanaRequest['headers'], - context: RequestHandlerContext, - req: KibanaRequest -) => Promise<{ - type: string | null; - title: string; - jobParams: JobParamsType; -}>; - -interface VisData { - title: string; - visType: string; - panel: SearchPanel; -} - -export const createJobFactory: CreateJobFactory> = function createJobFactoryFn(reporting: ReportingCore, parentLogger: LevelLogger) { - const config = reporting.getConfig(); - const crypto = cryptoFactory(config.get('encryptionKey')); - const logger = parentLogger.clone([CSV_FROM_SAVEDOBJECT_JOB_TYPE, 'create-job']); - - return async function createJob( - jobParams: JobParamsPanelCsv, - headers: KibanaRequest['headers'], - context: RequestHandlerContext, - req: KibanaRequest - ): Promise { - const { savedObjectType, savedObjectId } = jobParams; - const serializedEncryptedHeaders = await crypto.encrypt(headers); - - const { panel, title, visType }: VisData = await Promise.resolve() - .then(() => context.core.savedObjects.client.get(savedObjectType, savedObjectId)) - .then(async (savedObject: SavedObject) => { - const { attributes, references } = savedObject; - const { - kibanaSavedObjectMeta: kibanaSavedObjectMetaJSON, - } = attributes as SavedSearchObjectAttributesJSON; - const { timerange } = req.body as { timerange: TimeRangeParams }; - - if (!kibanaSavedObjectMetaJSON) { - throw new Error('Could not parse saved object data!'); - } - - const kibanaSavedObjectMeta = { - ...kibanaSavedObjectMetaJSON, - searchSource: JSON.parse(kibanaSavedObjectMetaJSON.searchSourceJSON), - }; - - const { visState: visStateJSON } = attributes as VisObjectAttributesJSON; - if (visStateJSON) { - throw notImplemented('Visualization types are not yet implemented'); - } - - // saved search type - return await createJobSearch(timerange, attributes, references, kibanaSavedObjectMeta); - }) - .catch((err: Error) => { - const boomErr = (err as unknown) as { isBoom: boolean }; - if (boomErr.isBoom) { - throw err; - } - const errPayload: SavedObjectServiceError = get(err, 'output.payload', { statusCode: 0 }); - if (errPayload.statusCode === 404) { - throw notFound(errPayload.message); - } - if (err.stack) { - logger.error(err.stack); - } - throw new Error(`Unable to create a job from saved object data! Error: ${err}`); - }); - - return { - headers: serializedEncryptedHeaders, - jobParams: { ...jobParams, panel, visType }, - type: null, - title, - }; - }; -}; diff --git a/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/server/create_job/index.ts b/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/server/create_job/index.ts index a3674d69ae6a5b..dafac040176075 100644 --- a/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/server/create_job/index.ts +++ b/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/server/create_job/index.ts @@ -4,4 +4,96 @@ * you may not use this file except in compliance with the Elastic License. */ -export { createJobFactory, ImmediateCreateJobFn } from './create_job'; +import { notFound, notImplemented } from 'boom'; +import { get } from 'lodash'; +import { KibanaRequest, RequestHandlerContext } from 'src/core/server'; +import { CSV_FROM_SAVEDOBJECT_JOB_TYPE } from '../../../../../common/constants'; +import { cryptoFactory } from '../../../../lib'; +import { ScheduleTaskFnFactory, TimeRangeParams } from '../../../../types'; +import { + JobParamsPanelCsv, + SavedObject, + SavedObjectServiceError, + SavedSearchObjectAttributesJSON, + SearchPanel, + VisObjectAttributesJSON, +} from '../../types'; +import { createJobSearch } from './create_job_search'; + +export type ImmediateCreateJobFn = ( + jobParams: JobParamsType, + headers: KibanaRequest['headers'], + context: RequestHandlerContext, + req: KibanaRequest +) => Promise<{ + type: string | null; + title: string; + jobParams: JobParamsType; +}>; + +interface VisData { + title: string; + visType: string; + panel: SearchPanel; +} + +export const scheduleTaskFnFactory: ScheduleTaskFnFactory> = function createJobFactoryFn(reporting, parentLogger) { + const config = reporting.getConfig(); + const crypto = cryptoFactory(config.get('encryptionKey')); + const logger = parentLogger.clone([CSV_FROM_SAVEDOBJECT_JOB_TYPE, 'create-job']); + + return async function scheduleTask(jobParams, headers, context, req) { + const { savedObjectType, savedObjectId } = jobParams; + const serializedEncryptedHeaders = await crypto.encrypt(headers); + + const { panel, title, visType }: VisData = await Promise.resolve() + .then(() => context.core.savedObjects.client.get(savedObjectType, savedObjectId)) + .then(async (savedObject: SavedObject) => { + const { attributes, references } = savedObject; + const { + kibanaSavedObjectMeta: kibanaSavedObjectMetaJSON, + } = attributes as SavedSearchObjectAttributesJSON; + const { timerange } = req.body as { timerange: TimeRangeParams }; + + if (!kibanaSavedObjectMetaJSON) { + throw new Error('Could not parse saved object data!'); + } + + const kibanaSavedObjectMeta = { + ...kibanaSavedObjectMetaJSON, + searchSource: JSON.parse(kibanaSavedObjectMetaJSON.searchSourceJSON), + }; + + const { visState: visStateJSON } = attributes as VisObjectAttributesJSON; + if (visStateJSON) { + throw notImplemented('Visualization types are not yet implemented'); + } + + // saved search type + return await createJobSearch(timerange, attributes, references, kibanaSavedObjectMeta); + }) + .catch((err: Error) => { + const boomErr = (err as unknown) as { isBoom: boolean }; + if (boomErr.isBoom) { + throw err; + } + const errPayload: SavedObjectServiceError = get(err, 'output.payload', { statusCode: 0 }); + if (errPayload.statusCode === 404) { + throw notFound(errPayload.message); + } + if (err.stack) { + logger.error(err.stack); + } + throw new Error(`Unable to create a job from saved object data! Error: ${err}`); + }); + + return { + headers: serializedEncryptedHeaders, + jobParams: { ...jobParams, panel, visType }, + type: null, + title, + }; + }; +}; diff --git a/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/server/execute_job.ts b/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/server/execute_job.ts index d555100b6320d3..26b7a24907f402 100644 --- a/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/server/execute_job.ts +++ b/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/server/execute_job.ts @@ -6,39 +6,33 @@ import { i18n } from '@kbn/i18n'; import { KibanaRequest, RequestHandlerContext } from 'src/core/server'; -import { ReportingCore } from '../../..'; import { CONTENT_TYPE_CSV, CSV_FROM_SAVEDOBJECT_JOB_TYPE } from '../../../../common/constants'; -import { cryptoFactory, LevelLogger } from '../../../lib'; -import { ExecuteJobFactory, JobDocOutput, JobDocPayload } from '../../../types'; +import { cryptoFactory } from '../../../lib'; +import { RunTaskFnFactory, ScheduledTaskParams, TaskRunResult } from '../../../types'; import { CsvResultFromSearch } from '../../csv/types'; -import { FakeRequest, JobDocPayloadPanelCsv, JobParamsPanelCsv, SearchPanel } from '../types'; +import { FakeRequest, JobParamsPanelCsv, SearchPanel } from '../types'; import { createGenerateCsv } from './lib'; /* * ImmediateExecuteFn receives the job doc payload because the payload was - * generated in the CreateFn + * generated in the ScheduleFn */ export type ImmediateExecuteFn = ( jobId: null, - job: JobDocPayload, + job: ScheduledTaskParams, context: RequestHandlerContext, req: KibanaRequest -) => Promise; +) => Promise; -export const executeJobFactory: ExecuteJobFactory> = async function executeJobFactoryFn(reporting: ReportingCore, parentLogger: LevelLogger) { +>> = function executeJobFactoryFn(reporting, parentLogger) { const config = reporting.getConfig(); const crypto = cryptoFactory(config.get('encryptionKey')); const logger = parentLogger.clone([CSV_FROM_SAVEDOBJECT_JOB_TYPE, 'execute-job']); const generateCsv = createGenerateCsv(reporting, parentLogger); - return async function executeJob( - jobId: string | null, - job: JobDocPayloadPanelCsv, - context, - req - ): Promise { + return async function runTask(jobId: string | null, job, context, req) { // There will not be a jobID for "immediate" generation. // jobID is only for "queued" jobs // Use the jobID as a logging tag or "immediate" diff --git a/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/types.d.ts b/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/types.d.ts index 36ae5b1dac05eb..835b352953dfeb 100644 --- a/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/types.d.ts +++ b/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/types.d.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { JobParamPostPayload, JobDocPayload, TimeRangeParams } from '../../types'; +import { JobParamPostPayload, ScheduledTaskParams, TimeRangeParams } from '../../types'; export interface FakeRequest { headers: Record; @@ -23,7 +23,7 @@ export interface JobParamsPanelCsv { visType?: string; } -export interface JobDocPayloadPanelCsv extends JobDocPayload { +export interface ScheduledTaskParamsPanelCsv extends ScheduledTaskParams { jobParams: JobParamsPanelCsv; } diff --git a/x-pack/plugins/reporting/server/export_types/png/index.ts b/x-pack/plugins/reporting/server/export_types/png/index.ts index a3b51e365e7727..b708448b0f8b24 100644 --- a/x-pack/plugins/reporting/server/export_types/png/index.ts +++ b/x-pack/plugins/reporting/server/export_types/png/index.ts @@ -14,22 +14,22 @@ import { } from '../../../common/constants'; import { ESQueueCreateJobFn, ESQueueWorkerExecuteFn, ExportTypeDefinition } from '../..//types'; import { metadata } from './metadata'; -import { createJobFactory } from './server/create_job'; -import { executeJobFactory } from './server/execute_job'; -import { JobDocPayloadPNG, JobParamsPNG } from './types'; +import { scheduleTaskFnFactory } from './server/create_job'; +import { runTaskFnFactory } from './server/execute_job'; +import { JobParamsPNG, ScheduledTaskParamsPNG } from './types'; export const getExportType = (): ExportTypeDefinition< JobParamsPNG, ESQueueCreateJobFn, - JobDocPayloadPNG, - ESQueueWorkerExecuteFn + ScheduledTaskParamsPNG, + ESQueueWorkerExecuteFn > => ({ ...metadata, jobType, jobContentEncoding: 'base64', jobContentExtension: 'PNG', - createJobFactory, - executeJobFactory, + scheduleTaskFnFactory, + runTaskFnFactory, validLicenses: [ LICENSE_TYPE_TRIAL, LICENSE_TYPE_STANDARD, diff --git a/x-pack/plugins/reporting/server/export_types/png/server/create_job/index.ts b/x-pack/plugins/reporting/server/export_types/png/server/create_job/index.ts index 3f1556fb297827..f459b8f249c706 100644 --- a/x-pack/plugins/reporting/server/export_types/png/server/create_job/index.ts +++ b/x-pack/plugins/reporting/server/export_types/png/server/create_job/index.ts @@ -6,17 +6,17 @@ import { validateUrls } from '../../../../../common/validate_urls'; import { cryptoFactory } from '../../../../lib'; -import { CreateJobFactory, ESQueueCreateJobFn } from '../../../../types'; +import { ESQueueCreateJobFn, ScheduleTaskFnFactory } from '../../../../types'; import { JobParamsPNG } from '../../types'; -export const createJobFactory: CreateJobFactory> = function createJobFactoryFn(reporting) { const config = reporting.getConfig(); const setupDeps = reporting.getPluginSetupDeps(); const crypto = cryptoFactory(config.get('encryptionKey')); - return async function createJob( + return async function scheduleTask( { objectType, title, relativeUrl, browserTimezone, layout }, context, req diff --git a/x-pack/plugins/reporting/server/export_types/png/server/execute_job/index.test.ts b/x-pack/plugins/reporting/server/export_types/png/server/execute_job/index.test.ts index b92f53ff6563f5..3d3f156aeef024 100644 --- a/x-pack/plugins/reporting/server/export_types/png/server/execute_job/index.test.ts +++ b/x-pack/plugins/reporting/server/export_types/png/server/execute_job/index.test.ts @@ -9,9 +9,9 @@ import { ReportingCore } from '../../../../'; import { CancellationToken } from '../../../../../common'; import { cryptoFactory, LevelLogger } from '../../../../lib'; import { createMockReportingCore } from '../../../../test_helpers'; -import { JobDocPayloadPNG } from '../../types'; +import { ScheduledTaskParamsPNG } from '../../types'; import { generatePngObservableFactory } from '../lib/generate_png'; -import { executeJobFactory } from './'; +import { runTaskFnFactory } from './'; jest.mock('../lib/generate_png', () => ({ generatePngObservableFactory: jest.fn() })); @@ -36,7 +36,7 @@ const encryptHeaders = async (headers: Record) => { return await crypto.encrypt(headers); }; -const getJobDocPayload = (baseObj: any) => baseObj as JobDocPayloadPNG; +const getScheduledTaskParams = (baseObj: any) => baseObj as ScheduledTaskParamsPNG; beforeEach(async () => { const kbnConfig = { @@ -81,11 +81,11 @@ test(`passes browserTimezone to generatePng`, async () => { const generatePngObservable = (await generatePngObservableFactory(mockReporting)) as jest.Mock; generatePngObservable.mockReturnValue(Rx.of(Buffer.from(''))); - const executeJob = await executeJobFactory(mockReporting, getMockLogger()); + const runTask = await runTaskFnFactory(mockReporting, getMockLogger()); const browserTimezone = 'UTC'; - await executeJob( + await runTask( 'pngJobId', - getJobDocPayload({ + getScheduledTaskParams({ relativeUrl: '/app/kibana#/something', browserTimezone, headers: encryptedHeaders, @@ -125,15 +125,15 @@ test(`passes browserTimezone to generatePng`, async () => { }); test(`returns content_type of application/png`, async () => { - const executeJob = await executeJobFactory(mockReporting, getMockLogger()); + const runTask = await runTaskFnFactory(mockReporting, getMockLogger()); const encryptedHeaders = await encryptHeaders({}); const generatePngObservable = await generatePngObservableFactory(mockReporting); (generatePngObservable as jest.Mock).mockReturnValue(Rx.of('foo')); - const { content_type: contentType } = await executeJob( + const { content_type: contentType } = await runTask( 'pngJobId', - getJobDocPayload({ relativeUrl: '/app/kibana#/something', headers: encryptedHeaders }), + getScheduledTaskParams({ relativeUrl: '/app/kibana#/something', headers: encryptedHeaders }), cancellationToken ); expect(contentType).toBe('image/png'); @@ -144,11 +144,11 @@ test(`returns content of generatePng getBuffer base64 encoded`, async () => { const generatePngObservable = await generatePngObservableFactory(mockReporting); (generatePngObservable as jest.Mock).mockReturnValue(Rx.of({ base64: testContent })); - const executeJob = await executeJobFactory(mockReporting, getMockLogger()); + const runTask = await runTaskFnFactory(mockReporting, getMockLogger()); const encryptedHeaders = await encryptHeaders({}); - const { content } = await executeJob( + const { content } = await runTask( 'pngJobId', - getJobDocPayload({ relativeUrl: '/app/kibana#/something', headers: encryptedHeaders }), + getScheduledTaskParams({ relativeUrl: '/app/kibana#/something', headers: encryptedHeaders }), cancellationToken ); diff --git a/x-pack/plugins/reporting/server/export_types/png/server/execute_job/index.ts b/x-pack/plugins/reporting/server/export_types/png/server/execute_job/index.ts index ea4c4b1d106ae5..c9ab890dc8a502 100644 --- a/x-pack/plugins/reporting/server/export_types/png/server/execute_job/index.ts +++ b/x-pack/plugins/reporting/server/export_types/png/server/execute_job/index.ts @@ -7,37 +7,35 @@ import apm from 'elastic-apm-node'; import * as Rx from 'rxjs'; import { catchError, map, mergeMap, takeUntil } from 'rxjs/operators'; -import { ReportingCore } from '../../../..'; import { PNG_JOB_TYPE } from '../../../../../common/constants'; -import { ESQueueWorkerExecuteFn, ExecuteJobFactory, JobDocOutput } from '../../../..//types'; -import { LevelLogger } from '../../../../lib'; +import { ESQueueWorkerExecuteFn, RunTaskFnFactory, TaskRunResult } from '../../../..//types'; import { decryptJobHeaders, getConditionalHeaders, getFullUrls, omitBlacklistedHeaders, } from '../../../common/execute_job/'; -import { JobDocPayloadPNG } from '../../types'; +import { ScheduledTaskParamsPNG } from '../../types'; import { generatePngObservableFactory } from '../lib/generate_png'; -type QueuedPngExecutorFactory = ExecuteJobFactory>; +type QueuedPngExecutorFactory = RunTaskFnFactory>; -export const executeJobFactory: QueuedPngExecutorFactory = async function executeJobFactoryFn( - reporting: ReportingCore, - parentLogger: LevelLogger +export const runTaskFnFactory: QueuedPngExecutorFactory = function executeJobFactoryFn( + reporting, + parentLogger ) { const config = reporting.getConfig(); const encryptionKey = config.get('encryptionKey'); const logger = parentLogger.clone([PNG_JOB_TYPE, 'execute']); - return async function executeJob(jobId: string, job: JobDocPayloadPNG, cancellationToken: any) { + return async function runTask(jobId, job, cancellationToken) { const apmTrans = apm.startTransaction('reporting execute_job png', 'reporting'); const apmGetAssets = apmTrans?.startSpan('get_assets', 'setup'); let apmGeneratePng: { end: () => void } | null | undefined; const generatePngObservable = await generatePngObservableFactory(reporting); const jobLogger = logger.clone([jobId]); - const process$: Rx.Observable = Rx.of(1).pipe( + const process$: Rx.Observable = Rx.of(1).pipe( mergeMap(() => decryptJobHeaders({ encryptionKey, job, logger })), map((decryptedHeaders) => omitBlacklistedHeaders({ job, decryptedHeaders })), map((filteredHeaders) => getConditionalHeaders({ config, job, filteredHeaders })), diff --git a/x-pack/plugins/reporting/server/export_types/png/types.d.ts b/x-pack/plugins/reporting/server/export_types/png/types.d.ts index 486a8e91a722f7..7a25f4ed8fe735 100644 --- a/x-pack/plugins/reporting/server/export_types/png/types.d.ts +++ b/x-pack/plugins/reporting/server/export_types/png/types.d.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { JobDocPayload } from '../../../server/types'; +import { ScheduledTaskParams } from '../../../server/types'; import { LayoutInstance, LayoutParams } from '../common/layouts'; // Job params: structure of incoming user request data @@ -17,7 +17,7 @@ export interface JobParamsPNG { } // Job payload: structure of stored job data provided by create_job -export interface JobDocPayloadPNG extends JobDocPayload { +export interface ScheduledTaskParamsPNG extends ScheduledTaskParams { basePath?: string; browserTimezone: string; forceNow?: string; diff --git a/x-pack/plugins/reporting/server/export_types/printable_pdf/index.ts b/x-pack/plugins/reporting/server/export_types/printable_pdf/index.ts index 39a0cbd5270a18..073bd38b538fbe 100644 --- a/x-pack/plugins/reporting/server/export_types/printable_pdf/index.ts +++ b/x-pack/plugins/reporting/server/export_types/printable_pdf/index.ts @@ -14,22 +14,22 @@ import { } from '../../../common/constants'; import { ESQueueCreateJobFn, ESQueueWorkerExecuteFn, ExportTypeDefinition } from '../../types'; import { metadata } from './metadata'; -import { createJobFactory } from './server/create_job'; -import { executeJobFactory } from './server/execute_job'; -import { JobDocPayloadPDF, JobParamsPDF } from './types'; +import { scheduleTaskFnFactory } from './server/create_job'; +import { runTaskFnFactory } from './server/execute_job'; +import { JobParamsPDF, ScheduledTaskParamsPDF } from './types'; export const getExportType = (): ExportTypeDefinition< JobParamsPDF, ESQueueCreateJobFn, - JobDocPayloadPDF, - ESQueueWorkerExecuteFn + ScheduledTaskParamsPDF, + ESQueueWorkerExecuteFn > => ({ ...metadata, jobType, jobContentEncoding: 'base64', jobContentExtension: 'pdf', - createJobFactory, - executeJobFactory, + scheduleTaskFnFactory, + runTaskFnFactory, validLicenses: [ LICENSE_TYPE_TRIAL, LICENSE_TYPE_STANDARD, diff --git a/x-pack/plugins/reporting/server/export_types/printable_pdf/server/create_job/index.ts b/x-pack/plugins/reporting/server/export_types/printable_pdf/server/create_job/index.ts index 06a0902a56954a..76c57182497202 100644 --- a/x-pack/plugins/reporting/server/export_types/printable_pdf/server/create_job/index.ts +++ b/x-pack/plugins/reporting/server/export_types/printable_pdf/server/create_job/index.ts @@ -6,18 +6,18 @@ import { validateUrls } from '../../../../../common/validate_urls'; import { cryptoFactory } from '../../../../lib'; -import { CreateJobFactory, ESQueueCreateJobFn } from '../../../../types'; +import { ESQueueCreateJobFn, ScheduleTaskFnFactory } from '../../../../types'; import { JobParamsPDF } from '../../types'; -export const createJobFactory: CreateJobFactory> = function createJobFactoryFn(reporting) { const config = reporting.getConfig(); const setupDeps = reporting.getPluginSetupDeps(); const crypto = cryptoFactory(config.get('encryptionKey')); - return async function createJobFn( - { title, relativeUrls, browserTimezone, layout, objectType }: JobParamsPDF, + return async function scheduleTaskFn( + { title, relativeUrls, browserTimezone, layout, objectType }, context, req ) { diff --git a/x-pack/plugins/reporting/server/export_types/printable_pdf/server/execute_job/index.test.ts b/x-pack/plugins/reporting/server/export_types/printable_pdf/server/execute_job/index.test.ts index 2f4ca47cf739e9..d4df8ef7072d3d 100644 --- a/x-pack/plugins/reporting/server/export_types/printable_pdf/server/execute_job/index.test.ts +++ b/x-pack/plugins/reporting/server/export_types/printable_pdf/server/execute_job/index.test.ts @@ -11,9 +11,9 @@ import { ReportingCore } from '../../../../'; import { CancellationToken } from '../../../../../common'; import { cryptoFactory, LevelLogger } from '../../../../lib'; import { createMockReportingCore } from '../../../../test_helpers'; -import { JobDocPayloadPDF } from '../../types'; +import { ScheduledTaskParamsPDF } from '../../types'; import { generatePdfObservableFactory } from '../lib/generate_pdf'; -import { executeJobFactory } from './'; +import { runTaskFnFactory } from './'; let mockReporting: ReportingCore; @@ -36,7 +36,7 @@ const encryptHeaders = async (headers: Record) => { return await crypto.encrypt(headers); }; -const getJobDocPayload = (baseObj: any) => baseObj as JobDocPayloadPDF; +const getScheduledTaskParams = (baseObj: any) => baseObj as ScheduledTaskParamsPDF; beforeEach(async () => { const kbnConfig = { @@ -79,11 +79,11 @@ test(`passes browserTimezone to generatePdf`, async () => { const generatePdfObservable = (await generatePdfObservableFactory(mockReporting)) as jest.Mock; generatePdfObservable.mockReturnValue(Rx.of(Buffer.from(''))); - const executeJob = await executeJobFactory(mockReporting, getMockLogger()); + const runTask = await runTaskFnFactory(mockReporting, getMockLogger()); const browserTimezone = 'UTC'; - await executeJob( + await runTask( 'pdfJobId', - getJobDocPayload({ + getScheduledTaskParams({ title: 'PDF Params Timezone Test', relativeUrl: '/app/kibana#/something', browserTimezone, @@ -98,15 +98,15 @@ test(`passes browserTimezone to generatePdf`, async () => { test(`returns content_type of application/pdf`, async () => { const logger = getMockLogger(); - const executeJob = await executeJobFactory(mockReporting, logger); + const runTask = await runTaskFnFactory(mockReporting, logger); const encryptedHeaders = await encryptHeaders({}); const generatePdfObservable = await generatePdfObservableFactory(mockReporting); (generatePdfObservable as jest.Mock).mockReturnValue(Rx.of(Buffer.from(''))); - const { content_type: contentType } = await executeJob( + const { content_type: contentType } = await runTask( 'pdfJobId', - getJobDocPayload({ relativeUrls: [], headers: encryptedHeaders }), + getScheduledTaskParams({ relativeUrls: [], headers: encryptedHeaders }), cancellationToken ); expect(contentType).toBe('application/pdf'); @@ -117,11 +117,11 @@ test(`returns content of generatePdf getBuffer base64 encoded`, async () => { const generatePdfObservable = await generatePdfObservableFactory(mockReporting); (generatePdfObservable as jest.Mock).mockReturnValue(Rx.of({ buffer: Buffer.from(testContent) })); - const executeJob = await executeJobFactory(mockReporting, getMockLogger()); + const runTask = await runTaskFnFactory(mockReporting, getMockLogger()); const encryptedHeaders = await encryptHeaders({}); - const { content } = await executeJob( + const { content } = await runTask( 'pdfJobId', - getJobDocPayload({ relativeUrls: [], headers: encryptedHeaders }), + getScheduledTaskParams({ relativeUrls: [], headers: encryptedHeaders }), cancellationToken ); diff --git a/x-pack/plugins/reporting/server/export_types/printable_pdf/server/execute_job/index.ts b/x-pack/plugins/reporting/server/export_types/printable_pdf/server/execute_job/index.ts index a4d84b2f9f1e04..7f8f2f4f6906ae 100644 --- a/x-pack/plugins/reporting/server/export_types/printable_pdf/server/execute_job/index.ts +++ b/x-pack/plugins/reporting/server/export_types/printable_pdf/server/execute_job/index.ts @@ -7,10 +7,8 @@ import apm from 'elastic-apm-node'; import * as Rx from 'rxjs'; import { catchError, map, mergeMap, takeUntil } from 'rxjs/operators'; -import { ReportingCore } from '../../../..'; import { PDF_JOB_TYPE } from '../../../../../common/constants'; -import { LevelLogger } from '../../../../lib'; -import { ESQueueWorkerExecuteFn, ExecuteJobFactory, JobDocOutput } from '../../../../types'; +import { ESQueueWorkerExecuteFn, RunTaskFnFactory, TaskRunResult } from '../../../../types'; import { decryptJobHeaders, getConditionalHeaders, @@ -18,21 +16,21 @@ import { getFullUrls, omitBlacklistedHeaders, } from '../../../common/execute_job'; -import { JobDocPayloadPDF } from '../../types'; +import { ScheduledTaskParamsPDF } from '../../types'; import { generatePdfObservableFactory } from '../lib/generate_pdf'; -type QueuedPdfExecutorFactory = ExecuteJobFactory>; +type QueuedPdfExecutorFactory = RunTaskFnFactory>; -export const executeJobFactory: QueuedPdfExecutorFactory = async function executeJobFactoryFn( - reporting: ReportingCore, - parentLogger: LevelLogger +export const runTaskFnFactory: QueuedPdfExecutorFactory = function executeJobFactoryFn( + reporting, + parentLogger ) { const config = reporting.getConfig(); const encryptionKey = config.get('encryptionKey'); const logger = parentLogger.clone([PDF_JOB_TYPE, 'execute']); - return async function executeJob(jobId: string, job: JobDocPayloadPDF, cancellationToken: any) { + return async function runTask(jobId, job, cancellationToken) { const apmTrans = apm.startTransaction('reporting execute_job pdf', 'reporting'); const apmGetAssets = apmTrans?.startSpan('get_assets', 'setup'); let apmGeneratePdf: { end: () => void } | null | undefined; @@ -40,7 +38,7 @@ export const executeJobFactory: QueuedPdfExecutorFactory = async function execut const generatePdfObservable = await generatePdfObservableFactory(reporting); const jobLogger = logger.clone([jobId]); - const process$: Rx.Observable = Rx.of(1).pipe( + const process$: Rx.Observable = Rx.of(1).pipe( mergeMap(() => decryptJobHeaders({ encryptionKey, job, logger })), map((decryptedHeaders) => omitBlacklistedHeaders({ job, decryptedHeaders })), map((filteredHeaders) => getConditionalHeaders({ config, job, filteredHeaders })), diff --git a/x-pack/plugins/reporting/server/export_types/printable_pdf/types.d.ts b/x-pack/plugins/reporting/server/export_types/printable_pdf/types.d.ts index 087ef5a6ca82c7..5399781a77753e 100644 --- a/x-pack/plugins/reporting/server/export_types/printable_pdf/types.d.ts +++ b/x-pack/plugins/reporting/server/export_types/printable_pdf/types.d.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { JobDocPayload } from '../../../server/types'; +import { ScheduledTaskParams } from '../../../server/types'; import { LayoutInstance, LayoutParams } from '../common/layouts'; // Job params: structure of incoming user request data, after being parsed from RISON @@ -17,7 +17,7 @@ export interface JobParamsPDF { } // Job payload: structure of stored job data provided by create_job -export interface JobDocPayloadPDF extends JobDocPayload { +export interface ScheduledTaskParamsPDF extends ScheduledTaskParams { basePath?: string; browserTimezone: string; forceNow?: string; diff --git a/x-pack/plugins/reporting/server/lib/create_queue.ts b/x-pack/plugins/reporting/server/lib/create_queue.ts index d993a17c0b3146..5d09af312a41b4 100644 --- a/x-pack/plugins/reporting/server/lib/create_queue.ts +++ b/x-pack/plugins/reporting/server/lib/create_queue.ts @@ -5,7 +5,7 @@ */ import { ReportingCore } from '../core'; -import { JobDocOutput, JobSource } from '../types'; +import { JobSource, TaskRunResult } from '../types'; import { createTaggedLogger } from './create_tagged_logger'; // TODO remove createTaggedLogger once esqueue is removed import { createWorkerFactory } from './create_worker'; import { Job } from './enqueue_job'; @@ -31,11 +31,11 @@ export interface ESQueueInstance { ) => ESQueueWorker; } -// GenericWorkerFn is a generic for ImmediateExecuteFn | ESQueueWorkerExecuteFn, +// GenericWorkerFn is a generic for ImmediateExecuteFn | ESQueueWorkerExecuteFn, type GenericWorkerFn = ( jobSource: JobSource, ...workerRestArgs: any[] -) => void | Promise; +) => void | Promise; export async function createQueueFactory( reporting: ReportingCore, diff --git a/x-pack/plugins/reporting/server/lib/create_worker.test.ts b/x-pack/plugins/reporting/server/lib/create_worker.test.ts index 8e1174e01aa7fe..85188c07eeb207 100644 --- a/x-pack/plugins/reporting/server/lib/create_worker.test.ts +++ b/x-pack/plugins/reporting/server/lib/create_worker.test.ts @@ -26,7 +26,7 @@ const executeJobFactoryStub = sinon.stub(); const getMockLogger = sinon.stub(); const getMockExportTypesRegistry = ( - exportTypes: any[] = [{ executeJobFactory: executeJobFactoryStub }] + exportTypes: any[] = [{ runTaskFnFactory: executeJobFactoryStub }] ) => ({ getAll: () => exportTypes, @@ -75,11 +75,11 @@ Object { test('Creates a single Esqueue worker for Reporting, even if there are multiple export types', async () => { const exportTypesRegistry = getMockExportTypesRegistry([ - { executeJobFactory: executeJobFactoryStub }, - { executeJobFactory: executeJobFactoryStub }, - { executeJobFactory: executeJobFactoryStub }, - { executeJobFactory: executeJobFactoryStub }, - { executeJobFactory: executeJobFactoryStub }, + { runTaskFnFactory: executeJobFactoryStub }, + { runTaskFnFactory: executeJobFactoryStub }, + { runTaskFnFactory: executeJobFactoryStub }, + { runTaskFnFactory: executeJobFactoryStub }, + { runTaskFnFactory: executeJobFactoryStub }, ]); mockReporting.getExportTypesRegistry = () => exportTypesRegistry; const createWorker = createWorkerFactory(mockReporting, getMockLogger()); diff --git a/x-pack/plugins/reporting/server/lib/create_worker.ts b/x-pack/plugins/reporting/server/lib/create_worker.ts index c9e865668bb301..837be1f44a0930 100644 --- a/x-pack/plugins/reporting/server/lib/create_worker.ts +++ b/x-pack/plugins/reporting/server/lib/create_worker.ts @@ -27,7 +27,7 @@ export function createWorkerFactory(reporting: ReportingCore, log for (const exportType of reporting.getExportTypesRegistry().getAll() as Array< ExportTypeDefinition> >) { - const jobExecutor = await exportType.executeJobFactory(reporting, logger); // FIXME: does not "need" to be async + const jobExecutor = exportType.runTaskFnFactory(reporting, logger); jobExecutors.set(exportType.jobType, jobExecutor); } diff --git a/x-pack/plugins/reporting/server/lib/enqueue_job.ts b/x-pack/plugins/reporting/server/lib/enqueue_job.ts index 3837f593df5b27..625da90f3b4f23 100644 --- a/x-pack/plugins/reporting/server/lib/enqueue_job.ts +++ b/x-pack/plugins/reporting/server/lib/enqueue_job.ts @@ -52,7 +52,7 @@ export function enqueueJobFactory( context: RequestHandlerContext, request: KibanaRequest ): Promise { - type CreateJobFn = ESQueueCreateJobFn; + type ScheduleTaskFnType = ESQueueCreateJobFn; const username = user ? user.username : false; const esqueue = await reporting.getEsqueue(); const exportType = reporting.getExportTypesRegistry().getById(exportTypeId); @@ -61,8 +61,8 @@ export function enqueueJobFactory( throw new Error(`Export type ${exportTypeId} does not exist in the registry!`); } - const createJob = exportType.createJobFactory(reporting, logger) as CreateJobFn; - const payload = await createJob(jobParams, context, request); + const scheduleTask = exportType.scheduleTaskFnFactory(reporting, logger) as ScheduleTaskFnType; + const payload = await scheduleTask(jobParams, context, request); const options = { timeout: queueTimeout, diff --git a/x-pack/plugins/reporting/server/lib/export_types_registry.ts b/x-pack/plugins/reporting/server/lib/export_types_registry.ts index 893a2635561ff4..501989f21103e7 100644 --- a/x-pack/plugins/reporting/server/lib/export_types_registry.ts +++ b/x-pack/plugins/reporting/server/lib/export_types_registry.ts @@ -5,15 +5,14 @@ */ import { isString } from 'lodash'; -import memoizeOne from 'memoize-one'; import { getExportType as getTypeCsv } from '../export_types/csv'; import { getExportType as getTypeCsvFromSavedObject } from '../export_types/csv_from_savedobject'; import { getExportType as getTypePng } from '../export_types/png'; import { getExportType as getTypePrintablePdf } from '../export_types/printable_pdf'; import { ExportTypeDefinition } from '../types'; -type GetCallbackFn = ( - item: ExportTypeDefinition +type GetCallbackFn = ( + item: ExportTypeDefinition ) => boolean; // => ExportTypeDefinition @@ -22,8 +21,8 @@ export class ExportTypesRegistry { constructor() {} - register( - item: ExportTypeDefinition + register( + item: ExportTypeDefinition ): void { if (!isString(item.id)) { throw new Error(`'item' must have a String 'id' property `); @@ -33,8 +32,6 @@ export class ExportTypesRegistry { throw new Error(`'item' with id ${item.id} has already been registered`); } - // TODO: Unwrap the execute function from the item's executeJobFactory - // Move that work out of server/lib/create_worker to reduce dependence on ESQueue this._map.set(item.id, item); } @@ -46,24 +43,24 @@ export class ExportTypesRegistry { return this._map.size; } - getById( + getById( id: string - ): ExportTypeDefinition { + ): ExportTypeDefinition { if (!this._map.has(id)) { throw new Error(`Unknown id ${id}`); } return this._map.get(id) as ExportTypeDefinition< JobParamsType, - CreateJobFnType, + ScheduleTaskFnType, JobPayloadType, - ExecuteJobFnType + RunTaskFnType >; } - get( - findType: GetCallbackFn - ): ExportTypeDefinition { + get( + findType: GetCallbackFn + ): ExportTypeDefinition { let result; for (const value of this._map.values()) { if (!findType(value)) { @@ -71,9 +68,9 @@ export class ExportTypesRegistry { } const foundResult: ExportTypeDefinition< JobParamsType, - CreateJobFnType, + ScheduleTaskFnType, JobPayloadType, - ExecuteJobFnType + RunTaskFnType > = value; if (result) { @@ -91,7 +88,7 @@ export class ExportTypesRegistry { } } -function getExportTypesRegistryFn(): ExportTypesRegistry { +export function getExportTypesRegistry(): ExportTypesRegistry { const registry = new ExportTypesRegistry(); /* this replaces the previously async method of registering export types, @@ -108,6 +105,3 @@ function getExportTypesRegistryFn(): ExportTypesRegistry { }); return registry; } - -// FIXME: is this the best way to return a singleton? -export const getExportTypesRegistry = memoizeOne(getExportTypesRegistryFn); diff --git a/x-pack/plugins/reporting/server/routes/generate_from_savedobject_immediate.ts b/x-pack/plugins/reporting/server/routes/generate_from_savedobject_immediate.ts index 1221f67855410e..7d93a36c85bc8f 100644 --- a/x-pack/plugins/reporting/server/routes/generate_from_savedobject_immediate.ts +++ b/x-pack/plugins/reporting/server/routes/generate_from_savedobject_immediate.ts @@ -7,12 +7,12 @@ import { schema } from '@kbn/config-schema'; import { ReportingCore } from '../'; import { API_BASE_GENERATE_V1 } from '../../common/constants'; -import { createJobFactory } from '../export_types/csv_from_savedobject/server/create_job'; -import { executeJobFactory } from '../export_types/csv_from_savedobject/server/execute_job'; +import { scheduleTaskFnFactory } from '../export_types/csv_from_savedobject/server/create_job'; +import { runTaskFnFactory } from '../export_types/csv_from_savedobject/server/execute_job'; import { getJobParamsFromRequest } from '../export_types/csv_from_savedobject/server/lib/get_job_params_from_request'; -import { JobDocPayloadPanelCsv } from '../export_types/csv_from_savedobject/types'; +import { ScheduledTaskParamsPanelCsv } from '../export_types/csv_from_savedobject/types'; import { LevelLogger as Logger } from '../lib'; -import { JobDocOutput } from '../types'; +import { TaskRunResult } from '../types'; import { authorizedUserPreRoutingFactory } from './lib/authorized_user_pre_routing'; import { HandlerErrorFunction } from './types'; @@ -36,8 +36,8 @@ export function registerGenerateCsvFromSavedObjectImmediate( /* * CSV export with the `immediate` option does not queue a job with Reporting's ESQueue to run the job async. Instead, this does: - * - re-use the createJob function to build up es query config - * - re-use the executeJob function to run the scan and scroll queries and capture the entire CSV in a result object. + * - re-use the scheduleTask function to build up es query config + * - re-use the runTask function to run the scan and scroll queries and capture the entire CSV in a result object. */ router.post( { @@ -60,11 +60,11 @@ export function registerGenerateCsvFromSavedObjectImmediate( userHandler(async (user, context, req, res) => { const logger = parentLogger.clone(['savedobject-csv']); const jobParams = getJobParamsFromRequest(req, { isImmediate: true }); - const createJobFn = createJobFactory(reporting, logger); - const executeJobFn = await executeJobFactory(reporting, logger); // FIXME: does not "need" to be async + const scheduleTaskFn = scheduleTaskFnFactory(reporting, logger); + const runTaskFn = runTaskFnFactory(reporting, logger); try { - const jobDocPayload: JobDocPayloadPanelCsv = await createJobFn( + const jobDocPayload: ScheduledTaskParamsPanelCsv = await scheduleTaskFn( jobParams, req.headers, context, @@ -74,13 +74,13 @@ export function registerGenerateCsvFromSavedObjectImmediate( content_type: jobOutputContentType, content: jobOutputContent, size: jobOutputSize, - }: JobDocOutput = await executeJobFn(null, jobDocPayload, context, req); + }: TaskRunResult = await runTaskFn(null, jobDocPayload, context, req); logger.info(`Job output size: ${jobOutputSize} bytes`); /* * ESQueue worker function defaults `content` to null, even if the - * executeJob returned undefined. + * runTask returned undefined. * * This converts null to undefined so the value can be sent to h.response() */ diff --git a/x-pack/plugins/reporting/server/routes/lib/get_document_payload.ts b/x-pack/plugins/reporting/server/routes/lib/get_document_payload.ts index e16f5278c8cc71..93f79bfd892b99 100644 --- a/x-pack/plugins/reporting/server/routes/lib/get_document_payload.ts +++ b/x-pack/plugins/reporting/server/routes/lib/get_document_payload.ts @@ -10,7 +10,7 @@ import * as _ from 'lodash'; import { CSV_JOB_TYPE } from '../../../common/constants'; import { statuses } from '../../lib/esqueue/constants/statuses'; import { ExportTypesRegistry } from '../../lib/export_types_registry'; -import { ExportTypeDefinition, JobDocOutput, JobSource } from '../../types'; +import { ExportTypeDefinition, JobSource, TaskRunResult } from '../../types'; type ExportTypeType = ExportTypeDefinition; @@ -18,7 +18,7 @@ interface ErrorFromPayload { message: string; } -// A camelCase version of JobDocOutput +// A camelCase version of TaskRunResult interface Payload { statusCode: number; content: string | Buffer | ErrorFromPayload; @@ -31,7 +31,7 @@ const DEFAULT_TITLE = 'report'; const getTitle = (exportType: ExportTypeType, title?: string): string => `${title || DEFAULT_TITLE}.${exportType.jobContentExtension}`; -const getReportingHeaders = (output: JobDocOutput, exportType: ExportTypeType) => { +const getReportingHeaders = (output: TaskRunResult, exportType: ExportTypeType) => { const metaDataHeaders: Record = {}; if (exportType.jobType === CSV_JOB_TYPE) { @@ -55,7 +55,7 @@ export function getDocumentPayloadFactory(exportTypesRegistry: ExportTypesRegist } } - function getCompleted(output: JobDocOutput, jobType: string, title: string): Payload { + function getCompleted(output: TaskRunResult, jobType: string, title: string): Payload { const exportType = exportTypesRegistry.get((item: ExportTypeType) => item.jobType === jobType); const filename = getTitle(exportType, title); const headers = getReportingHeaders(output, exportType); @@ -73,7 +73,7 @@ export function getDocumentPayloadFactory(exportTypesRegistry: ExportTypesRegist // @TODO: These should be semantic HTTP codes as 500/503's indicate // error then these are really operating properly. - function getFailure(output: JobDocOutput): Payload { + function getFailure(output: TaskRunResult): Payload { return { statusCode: 500, content: { diff --git a/x-pack/plugins/reporting/server/routes/types.d.ts b/x-pack/plugins/reporting/server/routes/types.d.ts index 5eceed0a7f2ab4..607ce34ab94652 100644 --- a/x-pack/plugins/reporting/server/routes/types.d.ts +++ b/x-pack/plugins/reporting/server/routes/types.d.ts @@ -6,7 +6,7 @@ import { KibanaRequest, KibanaResponseFactory, RequestHandlerContext } from 'src/core/server'; import { AuthenticatedUser } from '../../../security/server'; -import { JobDocPayload } from '../types'; +import { ScheduledTaskParams } from '../types'; export type HandlerFunction = ( user: AuthenticatedUser | null, @@ -23,7 +23,7 @@ export interface QueuedJobPayload { error?: boolean; source: { job: { - payload: JobDocPayload; + payload: ScheduledTaskParams; }; }; } diff --git a/x-pack/plugins/reporting/server/types.ts b/x-pack/plugins/reporting/server/types.ts index 409a89899bee00..96eef81672610d 100644 --- a/x-pack/plugins/reporting/server/types.ts +++ b/x-pack/plugins/reporting/server/types.ts @@ -58,7 +58,7 @@ export interface JobParamPostPayload { timerange: TimeRangeParams; } -export interface JobDocPayload { +export interface ScheduledTaskParams { headers?: string; // serialized encrypted headers jobParams: JobParamsType; title: string; @@ -70,13 +70,13 @@ export interface JobSource { _index: string; _source: { jobtype: string; - output: JobDocOutput; - payload: JobDocPayload; + output: TaskRunResult; + payload: ScheduledTaskParams; status: JobStatus; }; } -export interface JobDocOutput { +export interface TaskRunResult { content_type: string; content: string | null; size: number; @@ -173,43 +173,43 @@ export type ReportingSetup = object; * Internal Types */ +export type CaptureConfig = ReportingConfigType['capture']; +export type ScrollConfig = ReportingConfigType['csv']['scroll']; + export type ESQueueCreateJobFn = ( jobParams: JobParamsType, context: RequestHandlerContext, request: KibanaRequest ) => Promise; -export type ESQueueWorkerExecuteFn = ( +export type ESQueueWorkerExecuteFn = ( jobId: string, - job: JobDocPayloadType, - cancellationToken?: CancellationToken + job: ScheduledTaskParamsType, + cancellationToken: CancellationToken ) => Promise; -export type CaptureConfig = ReportingConfigType['capture']; -export type ScrollConfig = ReportingConfigType['csv']['scroll']; - -export type CreateJobFactory = ( +export type ScheduleTaskFnFactory = ( reporting: ReportingCore, logger: LevelLogger -) => CreateJobFnType; +) => ScheduleTaskFnType; -export type ExecuteJobFactory = ( +export type RunTaskFnFactory = ( reporting: ReportingCore, logger: LevelLogger -) => Promise; // FIXME: does not "need" to be async +) => RunTaskFnType; export interface ExportTypeDefinition< JobParamsType, - CreateJobFnType, + ScheduleTaskFnType, JobPayloadType, - ExecuteJobFnType + RunTaskFnType > { id: string; name: string; jobType: string; jobContentEncoding?: string; jobContentExtension: string; - createJobFactory: CreateJobFactory; - executeJobFactory: ExecuteJobFactory; + scheduleTaskFnFactory: ScheduleTaskFnFactory; + runTaskFnFactory: RunTaskFnFactory; validLicenses: string[]; }