diff --git a/.eslintrc.js b/.eslintrc.js index 5ff1aba1f3ff7..c821bfd5a2404 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -186,8 +186,10 @@ module.exports = { 'x-pack/legacy/plugins/apm/**/*.js', 'test/*/config.ts', 'test/visual_regression/tests/**/*', - 'x-pack/test/visual_regression/tests/**/*', - 'x-pack/test/*/config.ts', + 'x-pack/test/*/{tests,test_suites,apis,apps}/**/*', + 'x-pack/test/*/*config.*ts', + 'x-pack/test/saved_object_api_integration/*/apis/**/*', + 'x-pack/test/ui_capabilities/*/tests/**/*', ], rules: { 'import/no-default-export': 'off', diff --git a/.i18nrc.json b/.i18nrc.json index 40e6ebbe3e969..0424b9528a204 100644 --- a/.i18nrc.json +++ b/.i18nrc.json @@ -24,7 +24,8 @@ "timelion": "src/legacy/core_plugins/timelion", "tagCloud": "src/legacy/core_plugins/tagcloud", "tsvb": "src/legacy/core_plugins/metrics", - "kbnESQuery": "packages/kbn-es-query" + "kbnESQuery": "packages/kbn-es-query", + "inspector": "src/plugins/inspector" }, "exclude": ["src/legacy/ui/ui_render/ui_render_mixin.js"], "translations": [] diff --git a/docs/code/code-basic-nav.asciidoc b/docs/code/code-basic-nav.asciidoc index 6b74790514255..77a56b24801cc 100644 --- a/docs/code/code-basic-nav.asciidoc +++ b/docs/code/code-basic-nav.asciidoc @@ -17,4 +17,9 @@ Clicking *Blame* shows the most recent commit per line. [role="screenshot"] image::images/code-blame.png[] +[float] +==== Branch selector +You can use the Branch selector to view different branches of a repo. Note that code intelligence and search index are not available for any branch other than master branch. + + include::code-semantic-nav.asciidoc[] diff --git a/docs/code/code-install-lang-server.asciidoc b/docs/code/code-install-lang-server.asciidoc index acc6c5a9347eb..1bb5a24f0c7f3 100644 --- a/docs/code/code-install-lang-server.asciidoc +++ b/docs/code/code-install-lang-server.asciidoc @@ -16,6 +16,10 @@ You can check the status of the language servers and get installation instructio [role="screenshot"] image::images/code-lang-server-status.png[] +[float] +=== Ctag language server +*Code* also uses a Ctag language server to generate symbol information and code intelligence when a dedicated language server is not available. The code intelligence information generated by the Ctag language server is less accurate but covers more languages. + include::code-basic-nav.asciidoc[] diff --git a/docs/dashboard.asciidoc b/docs/dashboard.asciidoc index 48189465bc3e7..df5348a0673dc 100644 --- a/docs/dashboard.asciidoc +++ b/docs/dashboard.asciidoc @@ -3,66 +3,84 @@ [partintro] -- -A Kibana _dashboard_ displays a collection of visualizations, searches, and {kibana-ref}/maps.html[maps]. -You can arrange, resize, and edit the dashboard content and then save the dashboard -so you can share it. -[role="screenshot"] -image:images/Dashboard_example.png[Example dashboard] +A {kib} _dashboard_ is a collection of visualizations, searches, and +maps, typically in real-time. Dashboards provide +at-a-glance insights into your data and enable you to drill down into details. --- +To start working with dashboards, click *Dashboard* in the side navigation. +With *Dashboard*, you can: -[[dashboard-getting-started]] -== Building a Dashboard +* <> +* <> +* <> +* <> +* <> + + +[role="screenshot"] +image:images/Dashboard_example.png[Example dashboard] -If you haven't yet indexed data into {es} or created an index pattern, -you'll be prompted to do so as you follow the steps for creating a dashboard. -Or, you can use one of the prebuilt sample data sets, available from the -Kibana home page. [float] [[dashboard-read-only-access]] === [xpack]#Read only access# -When you have insufficient privileges to create or save dashboards, the following -indicator in Kibana will be displayed. The buttons to create new dashboards or edit -existing dashboard won't be visible. For more information on granting access to -Kibana see <>. +If you see +the read-only icon in the application header, +then you don't have sufficient privileges to create and save dashboards. The buttons to create and edit +dashboards are not visible. For more information, see <>. [role="screenshot"] image::images/dashboard-read-only-badge.png[Example of Dashboard's read only access indicator in Kibana's header] [float] +[[dashboard-getting-started]] +=== Interact with dashboards + +When you open *Dashhboard*, you're presented an overview of your dashboards. +If you don't have any dashboards, you can add +<>, +which include pre-built dashboards. + +Once you open a dashboard, you can filter the data +by entering a search query, changing the time filter, or clicking +in the visualizations, searches, and maps. If a dashboard element has a stored query, +both queries are applied. + +-- + [[dashboard-create-new-dashboard]] -=== Creating a new Dashboard +== Create a dashboard + +To create a dashboard, you must have data indexed into {es}, an index pattern +to retrieve the data from {es}, and +visualizations, saved searches, or maps. If these don't exist, you're prompted to +add them as you create the dashboard. -. In the side navigation, click *Dashboard*. +For an end-to-end example, see <>. + +. Open *Dashboard.* . Click *Create new dashboard.* . Click *Add*. -. [[adding-visualizations-to-a-dashboard]]Use *Add Panels* to add visualizations -and saved searches to the dashboard. If you have a large number of -visualizations, you can filter the lists. +. Use *Add panels* to add elements to the dashboard. ++ +The visualizations, saved searches, and maps +are stored in panels that you can move and resize. A +menu in the upper right of the panel has options for customizing +the panel. You can add elements from +multiple indices, and the same element can appear in multiple dashboards. + [role="screenshot"] image:images/Dashboard_add_visualization.png[Example add visualization to dashboard] -. [[saving-dashboards]]When you're finished adding and arranging the panels, -go to the menu bar and click *Save*. - -. In *Save Dashboard*, enter a dashboard title and optionally a description. - -. To store the time period specified in the time filter, enable *Store time -with dashboard*. - -. Click *Save*. - -[[loading-a-saved-dashboard]] -To import, export, and delete dashboards, see <>. +. When you're finished adding and arranging the panels, +*Save* the dashboard. +[float] [[customizing-your-dashboard]] -== Arranging Dashboard Elements +=== Arrange dashboard elements -The visualizations and searches in a dashboard are stored in panels that you can move, -resize, edit, and delete. To start editing, click *Edit* in the menu bar. +In *Edit* mode, you can move, resize, customize, and delete panels to suit your needs. [[moving-containers]] * To move a panel, click and hold the panel header and drag to the new location. @@ -71,53 +89,58 @@ resize, edit, and delete. To start editing, click *Edit* in the menu bar. * To resize a panel, click the resize control on the lower right and drag to the new dimensions. -[[removing-containers]] -Additional commands for managing the panel and its contents -are in the gear menu in the upper right. - -[role="screenshot"] -image:images/Dashboard_Resize_Menu.png[Example dashboard] +* To toggle the use of margins and panel titles, use the *Options* menu in the upper left. -NOTE: Deleting a panel from a +* To delete a panel, open the panel menu and select *Delete from dashboard.* Deleting a panel from a dashboard does *not* delete the saved visualization or search. -[[viewing-detailed-information]] -== Inspecting a Visualization from the Dashboard -Many visualizations allow you to inspect the data and requests behind the -visualization. +[float] +[[sharing-dashboards]] +=== Share a dashboard + +[[embedding-dashboards]] +When you've finished your dashboard, you can share it with your teammates. +From the *Share* menu, you can: + +* Embed the code in a web page. Users must have Kibana access +to view an embedded dashboard. +* Share a direct link to a {kib} dashboard +* Generate a PDF report +* Generate a PNG report + +TIP: You can create a link to a dashboard by title by doing this: + +`${domain}/${basepath?}/app/kibana#/dashboards?title=${yourdashboardtitle}` + +TIP: When sharing a link to a dashboard snapshot, use the *Short URL*. Snapshot +URLs are long and can be problematic for Internet Explorer and other +tools. To create a short URL, you must have write access to {kib}. + +[float] +[[import-dashboards]] +=== Import and export dashboards -In the dashboard, expand the visualization's panel menu (or gear menu if in -*Edit* mode) and select *Inspect*. +To import and export dashboards, go to *Management > Saved Objects*. For details, +see <>. -The initial view shows the underlying data for the visualization. To view the -requests that were made for the visualization, choose *Requests* from the *View* -menu. +[float] +[[viewing-detailed-information]] +=== Inspect and edit elements -The views you'll see depend on the element that you inspect. +Many dashboard elements allow you to drill down into the data and requests +behind the element. Open the menu in the upper right of the panel and select *Inspect*. +The views you see depend on the element that you inspect. [role="screenshot"] -image:images/Dashboard_visualization_data.png[Example of visualization data] +image:images/Dashboard_inspect.png[Inspect in dashboard] +To open an element for editing, put the dashboard in *Edit* mode, +and then select *Edit* from the panel menu. The changes you make appear in +every dashboard that uses the element. -[[sharing-dashboards]] -== Sharing a Dashboard -You can either share a direct link to a Kibana dashboard, -or embed the dashboard in a web page. Users must have Kibana access -to view an embedded dashboard. -[[embedding-dashboards]] -. Open the dashboard you want to share. -. In the menu bar, click *Share*. -. Copy the link you want to share or the iframe you want to embed. You can -share the live dashboard or a static snapshot of the current point in time. -TIP: You can create a link to a dashboard by title by doing this: + -`${domain}/${basepath?}/app/kibana#/dashboards?title=${yourdashboardtitle}` -TIP: When sharing a link to a dashboard snapshot, use the *Short URL*. Snapshot -URLs are long and can be problematic for Internet Explorer and other -tools. To create a short URL, you must have write access to {kib}. diff --git a/docs/development/core/development-basepath.asciidoc b/docs/development/core/development-basepath.asciidoc index e84171c79cb22..d49dfe2938fad 100644 --- a/docs/development/core/development-basepath.asciidoc +++ b/docs/development/core/development-basepath.asciidoc @@ -66,6 +66,15 @@ To accomplish this the `serve` task does a few things: - redirects from `/{any}/app/{appName}` to `/{randomBasePath}/app/{appName}` so that refreshes should work - proxies all requests starting with `/{randomBasePath}/` to the Kibana server +If you're writing scripts that interact with the Kibana API, the base path proxy will likely +make this difficult. To bypass the base path proxy for a single request, prefix urls with +`__UNSAFE_bypassBasePath` and the request will be routed to the development Kibana server. + +["source","shell"] +----------- +curl "http://elastic:changeme@localhost:5601/__UNSAFE_bypassBasePath/api/status" +----------- + This proxy can sometimes have unintended side effects in development, so when needed you can opt out by passing the `--no-base-path` flag to the `serve` task or `yarn start`. diff --git a/docs/development/core/server/kibana-plugin-server.authstatus.md b/docs/development/core/server/kibana-plugin-server.authstatus.md new file mode 100644 index 0000000000000..e59ade4f73e38 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.authstatus.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [AuthStatus](./kibana-plugin-server.authstatus.md) + +## AuthStatus enum + +Status indicating an outcome of the authentication. + +Signature: + +```typescript +export declare enum AuthStatus +``` + +## Enumeration Members + +| Member | Value | Description | +| --- | --- | --- | +| authenticated | "authenticated" | auth interceptor successfully authenticated a user | +| unauthenticated | "unauthenticated" | auth interceptor failed user authentication | +| unknown | "unknown" | auth interceptor has not been registered | + diff --git a/docs/development/core/server/kibana-plugin-server.coresetup.http.md b/docs/development/core/server/kibana-plugin-server.coresetup.http.md index c9206b7a7e711..e5347dd7c6625 100644 --- a/docs/development/core/server/kibana-plugin-server.coresetup.http.md +++ b/docs/development/core/server/kibana-plugin-server.coresetup.http.md @@ -13,7 +13,6 @@ http: { registerAuth: HttpServiceSetup['registerAuth']; registerOnPostAuth: HttpServiceSetup['registerOnPostAuth']; basePath: HttpServiceSetup['basePath']; - createNewServer: HttpServiceSetup['createNewServer']; isTlsEnabled: HttpServiceSetup['isTlsEnabled']; }; ``` diff --git a/docs/development/core/server/kibana-plugin-server.coresetup.md b/docs/development/core/server/kibana-plugin-server.coresetup.md index f4653d7f43579..38990f2797677 100644 --- a/docs/development/core/server/kibana-plugin-server.coresetup.md +++ b/docs/development/core/server/kibana-plugin-server.coresetup.md @@ -17,5 +17,5 @@ export interface CoreSetup | Property | Type | Description | | --- | --- | --- | | [elasticsearch](./kibana-plugin-server.coresetup.elasticsearch.md) | {
adminClient$: Observable<ClusterClient>;
dataClient$: Observable<ClusterClient>;
createClient: (type: string, clientConfig?: Partial<ElasticsearchClientConfig>) => ClusterClient;
} | | -| [http](./kibana-plugin-server.coresetup.http.md) | {
createCookieSessionStorageFactory: HttpServiceSetup['createCookieSessionStorageFactory'];
registerOnPreAuth: HttpServiceSetup['registerOnPreAuth'];
registerAuth: HttpServiceSetup['registerAuth'];
registerOnPostAuth: HttpServiceSetup['registerOnPostAuth'];
basePath: HttpServiceSetup['basePath'];
createNewServer: HttpServiceSetup['createNewServer'];
isTlsEnabled: HttpServiceSetup['isTlsEnabled'];
} | | +| [http](./kibana-plugin-server.coresetup.http.md) | {
createCookieSessionStorageFactory: HttpServiceSetup['createCookieSessionStorageFactory'];
registerOnPreAuth: HttpServiceSetup['registerOnPreAuth'];
registerAuth: HttpServiceSetup['registerAuth'];
registerOnPostAuth: HttpServiceSetup['registerOnPostAuth'];
basePath: HttpServiceSetup['basePath'];
isTlsEnabled: HttpServiceSetup['isTlsEnabled'];
} | | diff --git a/docs/development/core/server/kibana-plugin-server.customhttpresponseoptions.md b/docs/development/core/server/kibana-plugin-server.customhttpresponseoptions.md new file mode 100644 index 0000000000000..cabee8a47e5ca --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.customhttpresponseoptions.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [CustomHttpResponseOptions](./kibana-plugin-server.customhttpresponseoptions.md) + +## CustomHttpResponseOptions interface + +HTTP response parameters for a response with adjustable status code. + +Signature: + +```typescript +export interface CustomHttpResponseOptions extends HttpResponseOptions +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [statusCode](./kibana-plugin-server.customhttpresponseoptions.statuscode.md) | number | | + diff --git a/docs/development/core/server/kibana-plugin-server.customhttpresponseoptions.statuscode.md b/docs/development/core/server/kibana-plugin-server.customhttpresponseoptions.statuscode.md new file mode 100644 index 0000000000000..5444ccd2ebb55 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.customhttpresponseoptions.statuscode.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [CustomHttpResponseOptions](./kibana-plugin-server.customhttpresponseoptions.md) > [statusCode](./kibana-plugin-server.customhttpresponseoptions.statuscode.md) + +## CustomHttpResponseOptions.statusCode property + +Signature: + +```typescript +statusCode: number; +``` diff --git a/docs/development/core/server/kibana-plugin-server.getauthheaders.md b/docs/development/core/server/kibana-plugin-server.getauthheaders.md index ee7572615fe1a..fba8b8ca8ee3a 100644 --- a/docs/development/core/server/kibana-plugin-server.getauthheaders.md +++ b/docs/development/core/server/kibana-plugin-server.getauthheaders.md @@ -9,5 +9,5 @@ Get headers to authenticate a user against Elasticsearch. Signature: ```typescript -export declare type GetAuthHeaders = (request: KibanaRequest | Request) => AuthHeaders | undefined; +export declare type GetAuthHeaders = (request: KibanaRequest | LegacyRequest) => AuthHeaders | undefined; ``` diff --git a/docs/development/core/server/kibana-plugin-server.getauthstate.md b/docs/development/core/server/kibana-plugin-server.getauthstate.md new file mode 100644 index 0000000000000..47fc38c28f5e0 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.getauthstate.md @@ -0,0 +1,16 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [GetAuthState](./kibana-plugin-server.getauthstate.md) + +## GetAuthState type + +Get authentication state for a request. Returned by `auth` interceptor. + +Signature: + +```typescript +export declare type GetAuthState = (request: KibanaRequest | LegacyRequest) => { + status: AuthStatus; + state: unknown; +}; +``` diff --git a/docs/development/core/server/kibana-plugin-server.headers.md b/docs/development/core/server/kibana-plugin-server.headers.md index 83259efe8b79d..cd73d4de43b9d 100644 --- a/docs/development/core/server/kibana-plugin-server.headers.md +++ b/docs/development/core/server/kibana-plugin-server.headers.md @@ -4,9 +4,14 @@ ## Headers type +Http request headers to read. Signature: ```typescript -export declare type Headers = Record; +export declare type Headers = { + [header in KnownHeaders]?: string | string[] | undefined; +} & { + [header: string]: string | string[] | undefined; +}; ``` diff --git a/docs/development/core/server/kibana-plugin-server.httpresponseoptions.headers.md b/docs/development/core/server/kibana-plugin-server.httpresponseoptions.headers.md new file mode 100644 index 0000000000000..ee347f99a41a4 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.httpresponseoptions.headers.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [HttpResponseOptions](./kibana-plugin-server.httpresponseoptions.md) > [headers](./kibana-plugin-server.httpresponseoptions.headers.md) + +## HttpResponseOptions.headers property + +HTTP Headers with additional information about response + +Signature: + +```typescript +headers?: ResponseHeaders; +``` diff --git a/docs/development/core/server/kibana-plugin-server.httpresponseoptions.md b/docs/development/core/server/kibana-plugin-server.httpresponseoptions.md new file mode 100644 index 0000000000000..8f9ccf22c8c6d --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.httpresponseoptions.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [HttpResponseOptions](./kibana-plugin-server.httpresponseoptions.md) + +## HttpResponseOptions interface + +HTTP response parameters + +Signature: + +```typescript +export interface HttpResponseOptions +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [headers](./kibana-plugin-server.httpresponseoptions.headers.md) | ResponseHeaders | HTTP Headers with additional information about response | + diff --git a/docs/development/core/server/kibana-plugin-server.httpresponsepayload.md b/docs/development/core/server/kibana-plugin-server.httpresponsepayload.md new file mode 100644 index 0000000000000..3dc4e2c7956f7 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.httpresponsepayload.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [HttpResponsePayload](./kibana-plugin-server.httpresponsepayload.md) + +## HttpResponsePayload type + +Data send to the client as a response payload. + +Signature: + +```typescript +export declare type HttpResponsePayload = undefined | string | Record | Buffer | Stream; +``` diff --git a/docs/development/core/server/kibana-plugin-server.httpserversetup.auth.md b/docs/development/core/server/kibana-plugin-server.httpserversetup.auth.md new file mode 100644 index 0000000000000..e39c3c6316768 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.httpserversetup.auth.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [HttpServerSetup](./kibana-plugin-server.httpserversetup.md) > [auth](./kibana-plugin-server.httpserversetup.auth.md) + +## HttpServerSetup.auth property + +Signature: + +```typescript +auth: { + get: GetAuthState; + isAuthenticated: IsAuthenticated; + getAuthHeaders: GetAuthHeaders; + }; +``` diff --git a/docs/development/core/server/kibana-plugin-server.httpserversetup.basepath.md b/docs/development/core/server/kibana-plugin-server.httpserversetup.basepath.md new file mode 100644 index 0000000000000..5cfb2f5c4e8b4 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.httpserversetup.basepath.md @@ -0,0 +1,16 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [HttpServerSetup](./kibana-plugin-server.httpserversetup.md) > [basePath](./kibana-plugin-server.httpserversetup.basepath.md) + +## HttpServerSetup.basePath property + +Signature: + +```typescript +basePath: { + get: (request: KibanaRequest | LegacyRequest) => string; + set: (request: KibanaRequest | LegacyRequest, basePath: string) => void; + prepend: (url: string) => string; + remove: (url: string) => string; + }; +``` diff --git a/docs/development/core/server/kibana-plugin-server.httpserversetup.createcookiesessionstoragefactory.md b/docs/development/core/server/kibana-plugin-server.httpserversetup.createcookiesessionstoragefactory.md new file mode 100644 index 0000000000000..3dc01a52a2f58 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.httpserversetup.createcookiesessionstoragefactory.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [HttpServerSetup](./kibana-plugin-server.httpserversetup.md) > [createCookieSessionStorageFactory](./kibana-plugin-server.httpserversetup.createcookiesessionstoragefactory.md) + +## HttpServerSetup.createCookieSessionStorageFactory property + +Creates cookie based session storage factory [SessionStorageFactory](./kibana-plugin-server.sessionstoragefactory.md) + +Signature: + +```typescript +createCookieSessionStorageFactory: (cookieOptions: SessionStorageCookieOptions) => Promise>; +``` diff --git a/docs/development/core/server/kibana-plugin-server.httpserversetup.istlsenabled.md b/docs/development/core/server/kibana-plugin-server.httpserversetup.istlsenabled.md new file mode 100644 index 0000000000000..6961d4feeb7c7 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.httpserversetup.istlsenabled.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [HttpServerSetup](./kibana-plugin-server.httpserversetup.md) > [isTlsEnabled](./kibana-plugin-server.httpserversetup.istlsenabled.md) + +## HttpServerSetup.isTlsEnabled property + +Flag showing whether a server was configured to use TLS connection. + +Signature: + +```typescript +isTlsEnabled: boolean; +``` diff --git a/docs/development/core/server/kibana-plugin-server.httpserversetup.md b/docs/development/core/server/kibana-plugin-server.httpserversetup.md new file mode 100644 index 0000000000000..143ae66c0b32c --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.httpserversetup.md @@ -0,0 +1,93 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [HttpServerSetup](./kibana-plugin-server.httpserversetup.md) + +## HttpServerSetup interface + +Kibana HTTP Service provides own abstraction for work with HTTP stack. Plugins don't have direct access to `hapi` server and its primitives anymore. Moreover, plugins shouldn't rely on the fact that HTTP Service uses one or another library under the hood. This gives the platform flexibility to upgrade or changing our internal HTTP stack without breaking plugins. If the HTTP Service lacks functionality you need, we are happy to discuss and support your needs. + +Signature: + +```typescript +export interface HttpServerSetup +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [auth](./kibana-plugin-server.httpserversetup.auth.md) | {
get: GetAuthState;
isAuthenticated: IsAuthenticated;
getAuthHeaders: GetAuthHeaders;
} | | +| [basePath](./kibana-plugin-server.httpserversetup.basepath.md) | {
get: (request: KibanaRequest | LegacyRequest) => string;
set: (request: KibanaRequest | LegacyRequest, basePath: string) => void;
prepend: (url: string) => string;
remove: (url: string) => string;
} | | +| [createCookieSessionStorageFactory](./kibana-plugin-server.httpserversetup.createcookiesessionstoragefactory.md) | <T>(cookieOptions: SessionStorageCookieOptions<T>) => Promise<SessionStorageFactory<T>> | Creates cookie based session storage factory [SessionStorageFactory](./kibana-plugin-server.sessionstoragefactory.md) | +| [isTlsEnabled](./kibana-plugin-server.httpserversetup.istlsenabled.md) | boolean | Flag showing whether a server was configured to use TLS connection. | +| [registerAuth](./kibana-plugin-server.httpserversetup.registerauth.md) | (handler: AuthenticationHandler) => void | To define custom authentication and/or authorization mechanism for incoming requests. A handler should return a state to associate with the incoming request. The state can be retrieved later via http.auth.get(..) Only one AuthenticationHandler can be registered. | +| [registerOnPostAuth](./kibana-plugin-server.httpserversetup.registeronpostauth.md) | (handler: OnPostAuthHandler) => void | To define custom logic to perform for incoming requests. Runs the handler after Auth interceptor did make sure a user has access to the requested resource. The auth state is available at stage via http.auth.get(..) Can register any number of registerOnPreAuth, which are called in sequence (from the first registered to the last). | +| [registerOnPreAuth](./kibana-plugin-server.httpserversetup.registeronpreauth.md) | (handler: OnPreAuthHandler) => void | To define custom logic to perform for incoming requests. Runs the handler before Auth interceptor performs a check that user has access to requested resources, so it's the only place when you can forward a request to another URL right on the server. Can register any number of registerOnPostAuth, which are called in sequence (from the first registered to the last). | +| [registerRouter](./kibana-plugin-server.httpserversetup.registerrouter.md) | (router: Router) => void | Add all the routes registered with router to HTTP server request listeners. | +| [server](./kibana-plugin-server.httpserversetup.server.md) | Server | | + +## Example + +To handle an incoming request in your plugin you should: - Create a `Router` instance. Use `plugin-id` as a prefix path segment for your routes. + +```ts +import { Router } from 'src/core/server'; +const router = new Router('my-app'); + +``` +- Use `@kbn/config-schema` package to create a schema to validate the request `params`, `query`, and `body`. Every incoming request will be validated against the created schema. If validation failed, the request is rejected with `400` status and `Bad request` error without calling the route's handler. To opt out of validating the request, specify `false`. + +```ts +import { schema, TypeOf } from '@kbn/config-schema'; +const validate = { + params: schema.object({ + id: schema.string(), + }), +}; + +``` +- Declare a function to respond to incoming request. The function will receive `request` object containing request details: url, headers, matched route, as well as validated `params`, `query`, `body`. And `response` object instructing HTTP server to create HTTP response with information sent back to the client as the response body, headers, and HTTP status. Unlike, `hapi` route handler in the Legacy platform, any exception raised during the handler call will generate `500 Server error` response and log error details for further investigation. See below for returning custom error responses. + +```ts +const handler = async (request: KibanaRequest, response: ResponseFactory) => { + const data = await findObject(request.params.id); + // creates a command to respond with 'not found' error + if (!data) return response.notFound(); + // creates a command to send found data to the client and set response headers + return response.ok(data, { + headers: { + 'content-type': 'application/json' + } + }); +} + +``` +- Register route handler for GET request to 'my-app/path/{id}' path + +```ts +import { schema, TypeOf } from '@kbn/config-schema'; +import { Router } from 'src/core/server'; +const router = new Router('my-app'); + +const validate = { + params: schema.object({ + id: schema.string(), + }), +}; + +router.get({ + path: 'path/{id}', + validate +}, +async (request, response) => { + const data = await findObject(request.params.id); + if (!data) return response.notFound(); + return response.ok(data, { + headers: { + 'content-type': 'application/json' + } + }); +}); + +``` + diff --git a/docs/development/core/server/kibana-plugin-server.httpserversetup.registerauth.md b/docs/development/core/server/kibana-plugin-server.httpserversetup.registerauth.md new file mode 100644 index 0000000000000..6e63e0996a63a --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.httpserversetup.registerauth.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [HttpServerSetup](./kibana-plugin-server.httpserversetup.md) > [registerAuth](./kibana-plugin-server.httpserversetup.registerauth.md) + +## HttpServerSetup.registerAuth property + +To define custom authentication and/or authorization mechanism for incoming requests. A handler should return a state to associate with the incoming request. The state can be retrieved later via http.auth.get(..) Only one AuthenticationHandler can be registered. + +Signature: + +```typescript +registerAuth: (handler: AuthenticationHandler) => void; +``` diff --git a/docs/development/core/server/kibana-plugin-server.httpserversetup.registeronpostauth.md b/docs/development/core/server/kibana-plugin-server.httpserversetup.registeronpostauth.md new file mode 100644 index 0000000000000..c74a67da350ec --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.httpserversetup.registeronpostauth.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [HttpServerSetup](./kibana-plugin-server.httpserversetup.md) > [registerOnPostAuth](./kibana-plugin-server.httpserversetup.registeronpostauth.md) + +## HttpServerSetup.registerOnPostAuth property + +To define custom logic to perform for incoming requests. Runs the handler after Auth interceptor did make sure a user has access to the requested resource. The auth state is available at stage via http.auth.get(..) Can register any number of registerOnPreAuth, which are called in sequence (from the first registered to the last). + +Signature: + +```typescript +registerOnPostAuth: (handler: OnPostAuthHandler) => void; +``` diff --git a/docs/development/core/server/kibana-plugin-server.httpserversetup.registeronpreauth.md b/docs/development/core/server/kibana-plugin-server.httpserversetup.registeronpreauth.md new file mode 100644 index 0000000000000..f6efa1c1dd73c --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.httpserversetup.registeronpreauth.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [HttpServerSetup](./kibana-plugin-server.httpserversetup.md) > [registerOnPreAuth](./kibana-plugin-server.httpserversetup.registeronpreauth.md) + +## HttpServerSetup.registerOnPreAuth property + +To define custom logic to perform for incoming requests. Runs the handler before Auth interceptor performs a check that user has access to requested resources, so it's the only place when you can forward a request to another URL right on the server. Can register any number of registerOnPostAuth, which are called in sequence (from the first registered to the last). + +Signature: + +```typescript +registerOnPreAuth: (handler: OnPreAuthHandler) => void; +``` diff --git a/docs/development/core/server/kibana-plugin-server.httpserversetup.registerrouter.md b/docs/development/core/server/kibana-plugin-server.httpserversetup.registerrouter.md new file mode 100644 index 0000000000000..4c2a9ae327406 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.httpserversetup.registerrouter.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [HttpServerSetup](./kibana-plugin-server.httpserversetup.md) > [registerRouter](./kibana-plugin-server.httpserversetup.registerrouter.md) + +## HttpServerSetup.registerRouter property + +Add all the routes registered with `router` to HTTP server request listeners. + +Signature: + +```typescript +registerRouter: (router: Router) => void; +``` diff --git a/docs/development/core/server/kibana-plugin-server.httpserversetup.server.md b/docs/development/core/server/kibana-plugin-server.httpserversetup.server.md new file mode 100644 index 0000000000000..a137eba7c8a5a --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.httpserversetup.server.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [HttpServerSetup](./kibana-plugin-server.httpserversetup.md) > [server](./kibana-plugin-server.httpserversetup.server.md) + +## HttpServerSetup.server property + +Signature: + +```typescript +server: Server; +``` diff --git a/docs/development/core/server/kibana-plugin-server.httpservicesetup.createnewserver.md b/docs/development/core/server/kibana-plugin-server.httpservicesetup.createnewserver.md deleted file mode 100644 index e41684ea2b784..0000000000000 --- a/docs/development/core/server/kibana-plugin-server.httpservicesetup.createnewserver.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [HttpServiceSetup](./kibana-plugin-server.httpservicesetup.md) > [createNewServer](./kibana-plugin-server.httpservicesetup.createnewserver.md) - -## HttpServiceSetup.createNewServer property - -Signature: - -```typescript -createNewServer: (cfg: Partial) => Promise; -``` diff --git a/docs/development/core/server/kibana-plugin-server.httpservicesetup.md b/docs/development/core/server/kibana-plugin-server.httpservicesetup.md index ec4a2537b8404..7e8f17510c8ee 100644 --- a/docs/development/core/server/kibana-plugin-server.httpservicesetup.md +++ b/docs/development/core/server/kibana-plugin-server.httpservicesetup.md @@ -2,18 +2,11 @@ [Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [HttpServiceSetup](./kibana-plugin-server.httpservicesetup.md) -## HttpServiceSetup interface +## HttpServiceSetup type Signature: ```typescript -export interface HttpServiceSetup extends HttpServerSetup +export declare type HttpServiceSetup = HttpServerSetup; ``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [createNewServer](./kibana-plugin-server.httpservicesetup.createnewserver.md) | (cfg: Partial<HttpConfig>) => Promise<HttpServerSetup> | | - diff --git a/docs/development/core/server/kibana-plugin-server.isauthenticated.md b/docs/development/core/server/kibana-plugin-server.isauthenticated.md new file mode 100644 index 0000000000000..15f412710412a --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.isauthenticated.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [IsAuthenticated](./kibana-plugin-server.isauthenticated.md) + +## IsAuthenticated type + +Return authentication status for a request. + +Signature: + +```typescript +export declare type IsAuthenticated = (request: KibanaRequest | LegacyRequest) => boolean; +``` diff --git a/docs/development/core/server/kibana-plugin-server.kibanarequest.md b/docs/development/core/server/kibana-plugin-server.kibanarequest.md index a9622e4319d57..19167f2f64041 100644 --- a/docs/development/core/server/kibana-plugin-server.kibanarequest.md +++ b/docs/development/core/server/kibana-plugin-server.kibanarequest.md @@ -26,6 +26,6 @@ export declare class KibanaRequestHeaders | Readonly copy of incoming request headers. | | [params](./kibana-plugin-server.kibanarequest.params.md) | | Params | | | [query](./kibana-plugin-server.kibanarequest.query.md) | | Query | | -| [route](./kibana-plugin-server.kibanarequest.route.md) | | RecursiveReadonly<KibanaRequestRoute> | | -| [url](./kibana-plugin-server.kibanarequest.url.md) | | Url | | +| [route](./kibana-plugin-server.kibanarequest.route.md) | | RecursiveReadonly<KibanaRequestRoute> | matched route details | +| [url](./kibana-plugin-server.kibanarequest.url.md) | | Url | a WHATWG URL standard object. | diff --git a/docs/development/core/server/kibana-plugin-server.kibanarequest.route.md b/docs/development/core/server/kibana-plugin-server.kibanarequest.route.md index 301eeef1b6bb5..88954eedf4cfb 100644 --- a/docs/development/core/server/kibana-plugin-server.kibanarequest.route.md +++ b/docs/development/core/server/kibana-plugin-server.kibanarequest.route.md @@ -4,6 +4,8 @@ ## KibanaRequest.route property +matched route details + Signature: ```typescript diff --git a/docs/development/core/server/kibana-plugin-server.kibanarequest.url.md b/docs/development/core/server/kibana-plugin-server.kibanarequest.url.md index b8bd46199763e..62d1f97159476 100644 --- a/docs/development/core/server/kibana-plugin-server.kibanarequest.url.md +++ b/docs/development/core/server/kibana-plugin-server.kibanarequest.url.md @@ -4,6 +4,8 @@ ## KibanaRequest.url property +a WHATWG URL standard object. + Signature: ```typescript diff --git a/docs/development/core/server/kibana-plugin-server.kibanaresponsefactory.md b/docs/development/core/server/kibana-plugin-server.kibanaresponsefactory.md new file mode 100644 index 0000000000000..82832ee9334a5 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.kibanaresponsefactory.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [KibanaResponseFactory](./kibana-plugin-server.kibanaresponsefactory.md) + +## KibanaResponseFactory type + +Creates an object containing request response payload, HTTP headers, error details, and other data transmitted to the client. + +Signature: + +```typescript +export declare type KibanaResponseFactory = typeof kibanaResponseFactory; +``` diff --git a/docs/development/core/server/kibana-plugin-server.knownheaders.md b/docs/development/core/server/kibana-plugin-server.knownheaders.md new file mode 100644 index 0000000000000..986794f3aaa61 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.knownheaders.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [KnownHeaders](./kibana-plugin-server.knownheaders.md) + +## KnownHeaders type + +Set of well-known HTTP headers. + +Signature: + +```typescript +export declare type KnownHeaders = KnownKeys; +``` diff --git a/docs/development/core/server/kibana-plugin-server.legacyrequest.md b/docs/development/core/server/kibana-plugin-server.legacyrequest.md index 6f67928faa52c..a794b3bbe87c7 100644 --- a/docs/development/core/server/kibana-plugin-server.legacyrequest.md +++ b/docs/development/core/server/kibana-plugin-server.legacyrequest.md @@ -2,12 +2,15 @@ [Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [LegacyRequest](./kibana-plugin-server.legacyrequest.md) -## LegacyRequest type +## LegacyRequest interface -Support Legacy platform request for the period of migration. +> Warning: This API is now obsolete. +> +> `hapi` request object, supported during migration process only for backward compatibility. +> Signature: ```typescript -export declare type LegacyRequest = Request; +export interface LegacyRequest extends Request ``` diff --git a/docs/development/core/server/kibana-plugin-server.md b/docs/development/core/server/kibana-plugin-server.md index bd4ef84723057..4cb19665a5dd6 100644 --- a/docs/development/core/server/kibana-plugin-server.md +++ b/docs/development/core/server/kibana-plugin-server.md @@ -17,12 +17,18 @@ The plugin integrates with the core system via lifecycle events: `setup` | [ClusterClient](./kibana-plugin-server.clusterclient.md) | Represents an Elasticsearch cluster API client and allows to call API on behalf of the internal Kibana user and the actual user that is derived from the request headers (via asScoped(...)). | | [ElasticsearchErrorHelpers](./kibana-plugin-server.elasticsearcherrorhelpers.md) | Helpers for working with errors returned from the Elasticsearch service.Since the internal data of errors are subject to change, consumers of the Elasticsearch service should always use these helpers to classify errors instead of checking error internals such as body.error.header[WWW-Authenticate] | | [KibanaRequest](./kibana-plugin-server.kibanarequest.md) | Kibana specific abstraction for an incoming request. | -| [Router](./kibana-plugin-server.router.md) | | +| [Router](./kibana-plugin-server.router.md) | Provides ability to declare a handler function for a particular path and HTTP request method. Each route can have only one handler functions, which is executed when the route is matched. | | [SavedObjectsErrorHelpers](./kibana-plugin-server.savedobjectserrorhelpers.md) | | | [SavedObjectsSchema](./kibana-plugin-server.savedobjectsschema.md) | | | [SavedObjectsSerializer](./kibana-plugin-server.savedobjectsserializer.md) | | | [ScopedClusterClient](./kibana-plugin-server.scopedclusterclient.md) | Serves the same purpose as "normal" ClusterClient but exposes additional callAsCurrentUser method that doesn't use credentials of the Kibana internal user (as callAsInternalUser does) to request Elasticsearch API, but rather passes HTTP headers extracted from the current user request to the API | +## Enumerations + +| Enumeration | Description | +| --- | --- | +| [AuthStatus](./kibana-plugin-server.authstatus.md) | Status indicating an outcome of the authentication. | + ## Interfaces | Interface | Description | @@ -32,14 +38,17 @@ The plugin integrates with the core system via lifecycle events: `setup` | [CallAPIOptions](./kibana-plugin-server.callapioptions.md) | The set of options that defines how API call should be made and result be processed. | | [CoreSetup](./kibana-plugin-server.coresetup.md) | Context passed to the plugins setup method. | | [CoreStart](./kibana-plugin-server.corestart.md) | Context passed to the plugins start method. | +| [CustomHttpResponseOptions](./kibana-plugin-server.customhttpresponseoptions.md) | HTTP response parameters for a response with adjustable status code. | | [DiscoveredPlugin](./kibana-plugin-server.discoveredplugin.md) | Small container object used to expose information about discovered plugins that may or may not have been started. | | [ElasticsearchError](./kibana-plugin-server.elasticsearcherror.md) | | | [ElasticsearchServiceSetup](./kibana-plugin-server.elasticsearchservicesetup.md) | | | [FakeRequest](./kibana-plugin-server.fakerequest.md) | Fake request object created manually by Kibana plugins. | -| [HttpServiceSetup](./kibana-plugin-server.httpservicesetup.md) | | +| [HttpResponseOptions](./kibana-plugin-server.httpresponseoptions.md) | HTTP response parameters | +| [HttpServerSetup](./kibana-plugin-server.httpserversetup.md) | Kibana HTTP Service provides own abstraction for work with HTTP stack. Plugins don't have direct access to hapi server and its primitives anymore. Moreover, plugins shouldn't rely on the fact that HTTP Service uses one or another library under the hood. This gives the platform flexibility to upgrade or changing our internal HTTP stack without breaking plugins. If the HTTP Service lacks functionality you need, we are happy to discuss and support your needs. | | [HttpServiceStart](./kibana-plugin-server.httpservicestart.md) | | | [InternalCoreStart](./kibana-plugin-server.internalcorestart.md) | | | [KibanaRequestRoute](./kibana-plugin-server.kibanarequestroute.md) | Request specific route information exposed to a handler. | +| [LegacyRequest](./kibana-plugin-server.legacyrequest.md) | | | [Logger](./kibana-plugin-server.logger.md) | Logger exposes all the necessary methods to log any type of information and this is the interface used by the logging consumers including plugins. | | [LoggerFactory](./kibana-plugin-server.loggerfactory.md) | The single purpose of LoggerFactory interface is to define a way to retrieve a context-based logger instance. | | [LogMeta](./kibana-plugin-server.logmeta.md) | Contextual metadata | @@ -50,7 +59,8 @@ The plugin integrates with the core system via lifecycle events: `setup` | [PluginsServiceSetup](./kibana-plugin-server.pluginsservicesetup.md) | | | [PluginsServiceStart](./kibana-plugin-server.pluginsservicestart.md) | | | [ResponseErrorMeta](./kibana-plugin-server.responseerrormeta.md) | Additional metadata to enhance error output or provide error details. | -| [RouteConfigOptions](./kibana-plugin-server.routeconfigoptions.md) | Route specific configuration. | +| [RouteConfig](./kibana-plugin-server.routeconfig.md) | Route specific configuration. | +| [RouteConfigOptions](./kibana-plugin-server.routeconfigoptions.md) | Additional route options. | | [SavedObject](./kibana-plugin-server.savedobject.md) | | | [SavedObjectAttributes](./kibana-plugin-server.savedobjectattributes.md) | | | [SavedObjectReference](./kibana-plugin-server.savedobjectreference.md) | A reference to another saved object. | @@ -58,18 +68,36 @@ The plugin integrates with the core system via lifecycle events: `setup` | [SavedObjectsBulkCreateObject](./kibana-plugin-server.savedobjectsbulkcreateobject.md) | | | [SavedObjectsBulkGetObject](./kibana-plugin-server.savedobjectsbulkgetobject.md) | | | [SavedObjectsBulkResponse](./kibana-plugin-server.savedobjectsbulkresponse.md) | | -| [SavedObjectsClientWrapperOptions](./kibana-plugin-server.savedobjectsclientwrapperoptions.md) | | +| [SavedObjectsClientProviderOptions](./kibana-plugin-server.savedobjectsclientprovideroptions.md) | Options to control the creation of the Saved Objects Client. | +| [SavedObjectsClientWrapperOptions](./kibana-plugin-server.savedobjectsclientwrapperoptions.md) | Options passed to each SavedObjectsClientWrapperFactory to aid in creating the wrapper instance. | | [SavedObjectsCreateOptions](./kibana-plugin-server.savedobjectscreateoptions.md) | | +| [SavedObjectsExportOptions](./kibana-plugin-server.savedobjectsexportoptions.md) | Options controlling the export operation. | | [SavedObjectsFindOptions](./kibana-plugin-server.savedobjectsfindoptions.md) | | | [SavedObjectsFindResponse](./kibana-plugin-server.savedobjectsfindresponse.md) | | +| [SavedObjectsImportConflictError](./kibana-plugin-server.savedobjectsimportconflicterror.md) | Represents a failure to import due to a conflict. | +| [SavedObjectsImportError](./kibana-plugin-server.savedobjectsimporterror.md) | Represents a failure to import. | +| [SavedObjectsImportMissingReferencesError](./kibana-plugin-server.savedobjectsimportmissingreferenceserror.md) | Represents a failure to import due to missing references. | +| [SavedObjectsImportOptions](./kibana-plugin-server.savedobjectsimportoptions.md) | Options to control the import operation. | +| [SavedObjectsImportResponse](./kibana-plugin-server.savedobjectsimportresponse.md) | The response describing the result of an import. | +| [SavedObjectsImportRetry](./kibana-plugin-server.savedobjectsimportretry.md) | Describes a retry operation for importing a saved object. | +| [SavedObjectsImportUnknownError](./kibana-plugin-server.savedobjectsimportunknownerror.md) | Represents a failure to import due to an unknown reason. | +| [SavedObjectsImportUnsupportedTypeError](./kibana-plugin-server.savedobjectsimportunsupportedtypeerror.md) | Represents a failure to import due to having an unsupported saved object type. | | [SavedObjectsMigrationVersion](./kibana-plugin-server.savedobjectsmigrationversion.md) | A dictionary of saved object type -> version used to determine what migrations need to be applied to a saved object. | | [SavedObjectsRawDoc](./kibana-plugin-server.savedobjectsrawdoc.md) | A raw document as represented directly in the saved object index. | +| [SavedObjectsResolveImportErrorsOptions](./kibana-plugin-server.savedobjectsresolveimporterrorsoptions.md) | Options to control the "resolve import" operation. | | [SavedObjectsService](./kibana-plugin-server.savedobjectsservice.md) | | | [SavedObjectsUpdateOptions](./kibana-plugin-server.savedobjectsupdateoptions.md) | | | [SavedObjectsUpdateResponse](./kibana-plugin-server.savedobjectsupdateresponse.md) | | | [SessionStorage](./kibana-plugin-server.sessionstorage.md) | Provides an interface to store and retrieve data across requests. | +| [SessionStorageCookieOptions](./kibana-plugin-server.sessionstoragecookieoptions.md) | Configuration used to create HTTP session storage based on top of cookie mechanism. | | [SessionStorageFactory](./kibana-plugin-server.sessionstoragefactory.md) | SessionStorage factory to bind one to an incoming request | +## Variables + +| Variable | Description | +| --- | --- | +| [kibanaResponseFactory](./kibana-plugin-server.kibanaresponsefactory.md) | Set of helpers used to create KibanaResponse to form HTTP response on an incoming request. Should be returned as a result of [RequestHandler](./kibana-plugin-server.requesthandler.md) execution. | + ## Type Aliases | Type Alias | Description | @@ -79,15 +107,22 @@ The plugin integrates with the core system via lifecycle events: `setup` | [AuthHeaders](./kibana-plugin-server.authheaders.md) | Auth Headers map | | [ElasticsearchClientConfig](./kibana-plugin-server.elasticsearchclientconfig.md) | | | [GetAuthHeaders](./kibana-plugin-server.getauthheaders.md) | Get headers to authenticate a user against Elasticsearch. | -| [Headers](./kibana-plugin-server.headers.md) | | -| [LegacyRequest](./kibana-plugin-server.legacyrequest.md) | Support Legacy platform request for the period of migration. | +| [GetAuthState](./kibana-plugin-server.getauthstate.md) | Get authentication state for a request. Returned by auth interceptor. | +| [Headers](./kibana-plugin-server.headers.md) | Http request headers to read. | +| [HttpResponsePayload](./kibana-plugin-server.httpresponsepayload.md) | Data send to the client as a response payload. | +| [HttpServiceSetup](./kibana-plugin-server.httpservicesetup.md) | | +| [IsAuthenticated](./kibana-plugin-server.isauthenticated.md) | Return authentication status for a request. | +| [KibanaResponseFactory](./kibana-plugin-server.kibanaresponsefactory.md) | Creates an object containing request response payload, HTTP headers, error details, and other data transmitted to the client. | +| [KnownHeaders](./kibana-plugin-server.knownheaders.md) | Set of well-known HTTP headers. | | [OnPostAuthHandler](./kibana-plugin-server.onpostauthhandler.md) | | | [OnPreAuthHandler](./kibana-plugin-server.onpreauthhandler.md) | | | [PluginInitializer](./kibana-plugin-server.plugininitializer.md) | The plugin export at the root of a plugin's server directory should conform to this interface. | | [PluginName](./kibana-plugin-server.pluginname.md) | Dedicated type for plugin name/id that is supposed to make Map/Set/Arrays that use it as a key or value more obvious. | | [RecursiveReadonly](./kibana-plugin-server.recursivereadonly.md) | | +| [RedirectResponseOptions](./kibana-plugin-server.redirectresponseoptions.md) | HTTP response parameters for redirection response | +| [RequestHandler](./kibana-plugin-server.requesthandler.md) | A function executed when route path matched requested resource path. Request handler is expected to return a result of one of [KibanaResponseFactory](./kibana-plugin-server.kibanaresponsefactory.md) functions. | | [ResponseError](./kibana-plugin-server.responseerror.md) | Error message and optional data send to the client in case of error. | | [RouteMethod](./kibana-plugin-server.routemethod.md) | The set of common HTTP methods supported by Kibana routing. | | [SavedObjectsClientContract](./kibana-plugin-server.savedobjectsclientcontract.md) | \#\# SavedObjectsClient errorsSince the SavedObjectsClient has its hands in everything we are a little paranoid about the way we present errors back to to application code. Ideally, all errors will be either:1. Caused by bad implementation (ie. undefined is not a function) and as such unpredictable 2. An error that has been classified and decorated appropriately by the decorators in [SavedObjectsErrorHelpers](./kibana-plugin-server.savedobjectserrorhelpers.md)Type 1 errors are inevitable, but since all expected/handle-able errors should be Type 2 the isXYZError() helpers exposed at SavedObjectsErrorHelpers should be used to understand and manage error responses from the SavedObjectsClient.Type 2 errors are decorated versions of the source error, so if the elasticsearch client threw an error it will be decorated based on its type. That means that rather than looking for error.body.error.type or doing substring checks on error.body.error.reason, just use the helpers to understand the meaning of the error:\`\`\`js if (SavedObjectsErrorHelpers.isNotFoundError(error)) { // handle 404 }if (SavedObjectsErrorHelpers.isNotAuthorizedError(error)) { // 401 handling should be automatic, but in case you wanted to know }// always rethrow the error unless you handle it throw error; \`\`\`\#\#\# 404s from missing indexFrom the perspective of application code and APIs the SavedObjectsClient is a black box that persists objects. One of the internal details that users have no control over is that we use an elasticsearch index for persistance and that index might be missing.At the time of writing we are in the process of transitioning away from the operating assumption that the SavedObjects index is always available. Part of this transition is handling errors resulting from an index missing. These used to trigger a 500 error in most cases, and in others cause 404s with different error messages.From my (Spencer) perspective, a 404 from the SavedObjectsApi is a 404; The object the request/call was targeting could not be found. This is why \#14141 takes special care to ensure that 404 errors are generic and don't distinguish between index missing or document missing.\#\#\# 503s from missing indexUnlike all other methods, create requests are supposed to succeed even when the Kibana index does not exist because it will be automatically created by elasticsearch. When that is not the case it is because Elasticsearch's action.auto_create_index setting prevents it from being created automatically so we throw a special 503 with the intention of informing the user that their Elasticsearch settings need to be updated.See [SavedObjectsErrorHelpers](./kibana-plugin-server.savedobjectserrorhelpers.md) | -| [SavedObjectsClientWrapperFactory](./kibana-plugin-server.savedobjectsclientwrapperfactory.md) | | +| [SavedObjectsClientWrapperFactory](./kibana-plugin-server.savedobjectsclientwrapperfactory.md) | Describes the factory used to create instances of Saved Objects Client Wrappers. | diff --git a/docs/development/core/server/kibana-plugin-server.redirectresponseoptions.md b/docs/development/core/server/kibana-plugin-server.redirectresponseoptions.md new file mode 100644 index 0000000000000..6fb0a5add2fb6 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.redirectresponseoptions.md @@ -0,0 +1,17 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [RedirectResponseOptions](./kibana-plugin-server.redirectresponseoptions.md) + +## RedirectResponseOptions type + +HTTP response parameters for redirection response + +Signature: + +```typescript +export declare type RedirectResponseOptions = HttpResponseOptions & { + headers: { + location: string; + }; +}; +``` diff --git a/docs/development/core/server/kibana-plugin-server.requesthandler.md b/docs/development/core/server/kibana-plugin-server.requesthandler.md new file mode 100644 index 0000000000000..b7e593c30f2f3 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.requesthandler.md @@ -0,0 +1,42 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [RequestHandler](./kibana-plugin-server.requesthandler.md) + +## RequestHandler type + +A function executed when route path matched requested resource path. Request handler is expected to return a result of one of [KibanaResponseFactory](./kibana-plugin-server.kibanaresponsefactory.md) functions. + +Signature: + +```typescript +export declare type RequestHandler

= (request: KibanaRequest, TypeOf, TypeOf>, response: KibanaResponseFactory) => KibanaResponse | Promise>; +``` + +## Example + + +```ts +const router = new Router('my-app'); +// creates a route handler for GET request on 'my-app/path/{id}' path +router.get( + { + path: 'path/{id}', + // defines a validation schema for a named segment of the route path + validate: { + params: schema.object({ + id: schema.string(), + }), + }, + }, + // function to execute to create a responses + async (request, response) => { + const data = await findObject(request.params.id); + // creates a command to respond with 'not found' error + if (!data) return response.notFound(); + // creates a command to send found data to the client + return response.ok(data); + } +); + +``` + diff --git a/docs/development/core/server/kibana-plugin-server.routeconfig.md b/docs/development/core/server/kibana-plugin-server.routeconfig.md new file mode 100644 index 0000000000000..87ec365dc2510 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.routeconfig.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [RouteConfig](./kibana-plugin-server.routeconfig.md) + +## RouteConfig interface + +Route specific configuration. + +Signature: + +```typescript +export interface RouteConfig

+``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [options](./kibana-plugin-server.routeconfig.options.md) | RouteConfigOptions | Additional route options [RouteConfigOptions](./kibana-plugin-server.routeconfigoptions.md). | +| [path](./kibana-plugin-server.routeconfig.path.md) | string | The endpoint \_within\_ the router path to register the route. E.g. if the router is registered at /elasticsearch and the route path is /search, the full path for the route is /elasticsearch/search. Supports: - named path segments path/{name}. - optional path segments path/{position?}. - multi-segments path/{coordinates*2}. Segments are accessible within a handler function as params property of [KibanaRequest](./kibana-plugin-server.kibanarequest.md) object. To have read access to params you \*must\* specify validation schema with [RouteConfig.validate](./kibana-plugin-server.routeconfig.validate.md). | +| [validate](./kibana-plugin-server.routeconfig.validate.md) | RouteSchemas<P, Q, B> | false | A schema created with @kbn/config-schema that every request will be validated against. You \*must\* specify a validation schema to be able to read: - url path segments - request query - request body To opt out of validating the request, specify false. | + diff --git a/docs/development/core/server/kibana-plugin-server.routeconfig.options.md b/docs/development/core/server/kibana-plugin-server.routeconfig.options.md new file mode 100644 index 0000000000000..12ca36da6de7c --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.routeconfig.options.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [RouteConfig](./kibana-plugin-server.routeconfig.md) > [options](./kibana-plugin-server.routeconfig.options.md) + +## RouteConfig.options property + +Additional route options [RouteConfigOptions](./kibana-plugin-server.routeconfigoptions.md). + +Signature: + +```typescript +options?: RouteConfigOptions; +``` diff --git a/docs/development/core/server/kibana-plugin-server.routeconfig.path.md b/docs/development/core/server/kibana-plugin-server.routeconfig.path.md new file mode 100644 index 0000000000000..3437f0e0fe064 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.routeconfig.path.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [RouteConfig](./kibana-plugin-server.routeconfig.md) > [path](./kibana-plugin-server.routeconfig.path.md) + +## RouteConfig.path property + +The endpoint \_within\_ the router path to register the route. E.g. if the router is registered at `/elasticsearch` and the route path is `/search`, the full path for the route is `/elasticsearch/search`. Supports: - named path segments `path/{name}`. - optional path segments `path/{position?}`. - multi-segments `path/{coordinates*2}`. Segments are accessible within a handler function as `params` property of [KibanaRequest](./kibana-plugin-server.kibanarequest.md) object. To have read access to `params` you \*must\* specify validation schema with [RouteConfig.validate](./kibana-plugin-server.routeconfig.validate.md). + +Signature: + +```typescript +path: string; +``` diff --git a/docs/development/core/server/kibana-plugin-server.routeconfig.validate.md b/docs/development/core/server/kibana-plugin-server.routeconfig.validate.md new file mode 100644 index 0000000000000..f7177485f5fb6 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.routeconfig.validate.md @@ -0,0 +1,32 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [RouteConfig](./kibana-plugin-server.routeconfig.md) > [validate](./kibana-plugin-server.routeconfig.validate.md) + +## RouteConfig.validate property + +A schema created with `@kbn/config-schema` that every request will be validated against. You \*must\* specify a validation schema to be able to read: - url path segments - request query - request body To opt out of validating the request, specify `false`. + +Signature: + +```typescript +validate: RouteSchemas | false; +``` + +## Example + + +```ts + import { schema } from '@kbn/config-schema'; + router.get({ + path: 'path/{id}' + validate: { + params: schema.object({ + id: schema.string(), + }), + query: schema.object({...}), + body: schema.object({...}), + }, + }) + +``` + diff --git a/docs/development/core/server/kibana-plugin-server.routeconfigoptions.authrequired.md b/docs/development/core/server/kibana-plugin-server.routeconfigoptions.authrequired.md index 3fb4426c407cd..2bb2491cae5df 100644 --- a/docs/development/core/server/kibana-plugin-server.routeconfigoptions.authrequired.md +++ b/docs/development/core/server/kibana-plugin-server.routeconfigoptions.authrequired.md @@ -4,7 +4,7 @@ ## RouteConfigOptions.authRequired property -A flag shows that authentication for a route: enabled when true disabled when false +A flag shows that authentication for a route: `enabled` when true `disabled` when false Enabled by default. diff --git a/docs/development/core/server/kibana-plugin-server.routeconfigoptions.md b/docs/development/core/server/kibana-plugin-server.routeconfigoptions.md index 97e480c5490fc..b4d210ac0b711 100644 --- a/docs/development/core/server/kibana-plugin-server.routeconfigoptions.md +++ b/docs/development/core/server/kibana-plugin-server.routeconfigoptions.md @@ -4,7 +4,7 @@ ## RouteConfigOptions interface -Route specific configuration. +Additional route options. Signature: @@ -16,6 +16,6 @@ export interface RouteConfigOptions | Property | Type | Description | | --- | --- | --- | -| [authRequired](./kibana-plugin-server.routeconfigoptions.authrequired.md) | boolean | A flag shows that authentication for a route: enabled when true disabled when falseEnabled by default. | +| [authRequired](./kibana-plugin-server.routeconfigoptions.authrequired.md) | boolean | A flag shows that authentication for a route: enabled when true disabled when falseEnabled by default. | | [tags](./kibana-plugin-server.routeconfigoptions.tags.md) | readonly string[] | Additional metadata tag strings to attach to the route. | diff --git a/docs/development/core/server/kibana-plugin-server.router.(constructor).md b/docs/development/core/server/kibana-plugin-server.router.(constructor).md index 5f8e1e5e293ab..26048a603c9f6 100644 --- a/docs/development/core/server/kibana-plugin-server.router.(constructor).md +++ b/docs/development/core/server/kibana-plugin-server.router.(constructor).md @@ -16,5 +16,5 @@ constructor(path: string); | Parameter | Type | Description | | --- | --- | --- | -| path | string | | +| path | string | a router path, set as the very first path segment for all registered routes. | diff --git a/docs/development/core/server/kibana-plugin-server.router.delete.md b/docs/development/core/server/kibana-plugin-server.router.delete.md index cd49f80baaf70..565dc10ce76e8 100644 --- a/docs/development/core/server/kibana-plugin-server.router.delete.md +++ b/docs/development/core/server/kibana-plugin-server.router.delete.md @@ -4,7 +4,7 @@ ## Router.delete() method -Register a `DELETE` request with the router +Register a route handler for `DELETE` request. Signature: diff --git a/docs/development/core/server/kibana-plugin-server.router.get.md b/docs/development/core/server/kibana-plugin-server.router.get.md index ab8e7c8c5a65d..a3899eaa678f7 100644 --- a/docs/development/core/server/kibana-plugin-server.router.get.md +++ b/docs/development/core/server/kibana-plugin-server.router.get.md @@ -4,7 +4,7 @@ ## Router.get() method -Register a `GET` request with the router +Register a route handler for `GET` request. Signature: diff --git a/docs/development/core/server/kibana-plugin-server.router.getroutes.md b/docs/development/core/server/kibana-plugin-server.router.getroutes.md deleted file mode 100644 index 3e4785a3a7c6c..0000000000000 --- a/docs/development/core/server/kibana-plugin-server.router.getroutes.md +++ /dev/null @@ -1,19 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [Router](./kibana-plugin-server.router.md) > [getRoutes](./kibana-plugin-server.router.getroutes.md) - -## Router.getRoutes() method - -Returns all routes registered with the this router. - -Signature: - -```typescript -getRoutes(): Readonly[]; -``` -Returns: - -`Readonly[]` - -List of registered routes. - diff --git a/docs/development/core/server/kibana-plugin-server.router.md b/docs/development/core/server/kibana-plugin-server.router.md index 52193bbc553c7..a5eb1c66c4f0c 100644 --- a/docs/development/core/server/kibana-plugin-server.router.md +++ b/docs/development/core/server/kibana-plugin-server.router.md @@ -4,6 +4,7 @@ ## Router class +Provides ability to declare a handler function for a particular path and HTTP request method. Each route can have only one handler functions, which is executed when the route is matched. Signature: @@ -28,9 +29,18 @@ export declare class Router | Method | Modifiers | Description | | --- | --- | --- | -| [delete(route, handler)](./kibana-plugin-server.router.delete.md) | | Register a DELETE request with the router | -| [get(route, handler)](./kibana-plugin-server.router.get.md) | | Register a GET request with the router | -| [getRoutes()](./kibana-plugin-server.router.getroutes.md) | | Returns all routes registered with the this router. | -| [post(route, handler)](./kibana-plugin-server.router.post.md) | | Register a POST request with the router | -| [put(route, handler)](./kibana-plugin-server.router.put.md) | | Register a PUT request with the router | +| [delete(route, handler)](./kibana-plugin-server.router.delete.md) | | Register a route handler for DELETE request. | +| [get(route, handler)](./kibana-plugin-server.router.get.md) | | Register a route handler for GET request. | +| [post(route, handler)](./kibana-plugin-server.router.post.md) | | Register a route handler for POST request. | +| [put(route, handler)](./kibana-plugin-server.router.put.md) | | Register a route handler for PUT request. | + +## Example + + +```ts +const router = new Router('my-app'); +// handler is called when 'my-app/path' resource is requested with `GET` method +router.get({ path: '/path', validate: false }, (req, res) => res.ok({ content: 'ok' })); + +``` diff --git a/docs/development/core/server/kibana-plugin-server.router.post.md b/docs/development/core/server/kibana-plugin-server.router.post.md index a499a46b1ee79..7aca35466d643 100644 --- a/docs/development/core/server/kibana-plugin-server.router.post.md +++ b/docs/development/core/server/kibana-plugin-server.router.post.md @@ -4,7 +4,7 @@ ## Router.post() method -Register a `POST` request with the router +Register a route handler for `POST` request. Signature: diff --git a/docs/development/core/server/kibana-plugin-server.router.put.md b/docs/development/core/server/kibana-plugin-server.router.put.md index 7b1337279cca9..760ccf9ef88e8 100644 --- a/docs/development/core/server/kibana-plugin-server.router.put.md +++ b/docs/development/core/server/kibana-plugin-server.router.put.md @@ -4,7 +4,7 @@ ## Router.put() method -Register a `PUT` request with the router +Register a route handler for `PUT` request. Signature: diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsclientprovideroptions.excludedwrappers.md b/docs/development/core/server/kibana-plugin-server.savedobjectsclientprovideroptions.excludedwrappers.md new file mode 100644 index 0000000000000..9eb036c01e26e --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsclientprovideroptions.excludedwrappers.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsClientProviderOptions](./kibana-plugin-server.savedobjectsclientprovideroptions.md) > [excludedWrappers](./kibana-plugin-server.savedobjectsclientprovideroptions.excludedwrappers.md) + +## SavedObjectsClientProviderOptions.excludedWrappers property + +Signature: + +```typescript +excludedWrappers?: string[]; +``` diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsclientprovideroptions.md b/docs/development/core/server/kibana-plugin-server.savedobjectsclientprovideroptions.md new file mode 100644 index 0000000000000..29b872a30a373 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsclientprovideroptions.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsClientProviderOptions](./kibana-plugin-server.savedobjectsclientprovideroptions.md) + +## SavedObjectsClientProviderOptions interface + +Options to control the creation of the Saved Objects Client. + +Signature: + +```typescript +export interface SavedObjectsClientProviderOptions +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [excludedWrappers](./kibana-plugin-server.savedobjectsclientprovideroptions.excludedwrappers.md) | string[] | | + diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsclientwrapperfactory.md b/docs/development/core/server/kibana-plugin-server.savedobjectsclientwrapperfactory.md index 321aefcba0ffd..3ef2fac727b01 100644 --- a/docs/development/core/server/kibana-plugin-server.savedobjectsclientwrapperfactory.md +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsclientwrapperfactory.md @@ -4,6 +4,8 @@ ## SavedObjectsClientWrapperFactory type +Describes the factory used to create instances of Saved Objects Client Wrappers. + Signature: ```typescript diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsclientwrapperoptions.md b/docs/development/core/server/kibana-plugin-server.savedobjectsclientwrapperoptions.md index 1a096fd9e5264..65e7cfa64c2a6 100644 --- a/docs/development/core/server/kibana-plugin-server.savedobjectsclientwrapperoptions.md +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsclientwrapperoptions.md @@ -4,6 +4,8 @@ ## SavedObjectsClientWrapperOptions interface +Options passed to each SavedObjectsClientWrapperFactory to aid in creating the wrapper instance. + Signature: ```typescript diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsexportoptions.exportsizelimit.md b/docs/development/core/server/kibana-plugin-server.savedobjectsexportoptions.exportsizelimit.md new file mode 100644 index 0000000000000..d8ff7b4c9e2ed --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsexportoptions.exportsizelimit.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsExportOptions](./kibana-plugin-server.savedobjectsexportoptions.md) > [exportSizeLimit](./kibana-plugin-server.savedobjectsexportoptions.exportsizelimit.md) + +## SavedObjectsExportOptions.exportSizeLimit property + +Signature: + +```typescript +exportSizeLimit: number; +``` diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsexportoptions.includereferencesdeep.md b/docs/development/core/server/kibana-plugin-server.savedobjectsexportoptions.includereferencesdeep.md new file mode 100644 index 0000000000000..1972cc6634b75 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsexportoptions.includereferencesdeep.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsExportOptions](./kibana-plugin-server.savedobjectsexportoptions.md) > [includeReferencesDeep](./kibana-plugin-server.savedobjectsexportoptions.includereferencesdeep.md) + +## SavedObjectsExportOptions.includeReferencesDeep property + +Signature: + +```typescript +includeReferencesDeep?: boolean; +``` diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsexportoptions.md b/docs/development/core/server/kibana-plugin-server.savedobjectsexportoptions.md new file mode 100644 index 0000000000000..66f501a0f1433 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsexportoptions.md @@ -0,0 +1,25 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsExportOptions](./kibana-plugin-server.savedobjectsexportoptions.md) + +## SavedObjectsExportOptions interface + +Options controlling the export operation. + +Signature: + +```typescript +export interface SavedObjectsExportOptions +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [exportSizeLimit](./kibana-plugin-server.savedobjectsexportoptions.exportsizelimit.md) | number | | +| [includeReferencesDeep](./kibana-plugin-server.savedobjectsexportoptions.includereferencesdeep.md) | boolean | | +| [namespace](./kibana-plugin-server.savedobjectsexportoptions.namespace.md) | string | | +| [objects](./kibana-plugin-server.savedobjectsexportoptions.objects.md) | Array<{
id: string;
type: string;
}> | | +| [savedObjectsClient](./kibana-plugin-server.savedobjectsexportoptions.savedobjectsclient.md) | SavedObjectsClientContract | | +| [types](./kibana-plugin-server.savedobjectsexportoptions.types.md) | string[] | | + diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsexportoptions.namespace.md b/docs/development/core/server/kibana-plugin-server.savedobjectsexportoptions.namespace.md new file mode 100644 index 0000000000000..b5abfba7f6910 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsexportoptions.namespace.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsExportOptions](./kibana-plugin-server.savedobjectsexportoptions.md) > [namespace](./kibana-plugin-server.savedobjectsexportoptions.namespace.md) + +## SavedObjectsExportOptions.namespace property + +Signature: + +```typescript +namespace?: string; +``` diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsexportoptions.objects.md b/docs/development/core/server/kibana-plugin-server.savedobjectsexportoptions.objects.md new file mode 100644 index 0000000000000..46cb62841d46c --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsexportoptions.objects.md @@ -0,0 +1,14 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsExportOptions](./kibana-plugin-server.savedobjectsexportoptions.md) > [objects](./kibana-plugin-server.savedobjectsexportoptions.objects.md) + +## SavedObjectsExportOptions.objects property + +Signature: + +```typescript +objects?: Array<{ + id: string; + type: string; + }>; +``` diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsexportoptions.savedobjectsclient.md b/docs/development/core/server/kibana-plugin-server.savedobjectsexportoptions.savedobjectsclient.md new file mode 100644 index 0000000000000..fc206d0f7e877 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsexportoptions.savedobjectsclient.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsExportOptions](./kibana-plugin-server.savedobjectsexportoptions.md) > [savedObjectsClient](./kibana-plugin-server.savedobjectsexportoptions.savedobjectsclient.md) + +## SavedObjectsExportOptions.savedObjectsClient property + +Signature: + +```typescript +savedObjectsClient: SavedObjectsClientContract; +``` diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsexportoptions.types.md b/docs/development/core/server/kibana-plugin-server.savedobjectsexportoptions.types.md new file mode 100644 index 0000000000000..204402fe355e3 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsexportoptions.types.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsExportOptions](./kibana-plugin-server.savedobjectsexportoptions.md) > [types](./kibana-plugin-server.savedobjectsexportoptions.types.md) + +## SavedObjectsExportOptions.types property + +Signature: + +```typescript +types?: string[]; +``` diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsimportconflicterror.md b/docs/development/core/server/kibana-plugin-server.savedobjectsimportconflicterror.md new file mode 100644 index 0000000000000..485500da504a9 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsimportconflicterror.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsImportConflictError](./kibana-plugin-server.savedobjectsimportconflicterror.md) + +## SavedObjectsImportConflictError interface + +Represents a failure to import due to a conflict. + +Signature: + +```typescript +export interface SavedObjectsImportConflictError +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [type](./kibana-plugin-server.savedobjectsimportconflicterror.type.md) | 'conflict' | | + diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsimportconflicterror.type.md b/docs/development/core/server/kibana-plugin-server.savedobjectsimportconflicterror.type.md new file mode 100644 index 0000000000000..bd85de140674a --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsimportconflicterror.type.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsImportConflictError](./kibana-plugin-server.savedobjectsimportconflicterror.md) > [type](./kibana-plugin-server.savedobjectsimportconflicterror.type.md) + +## SavedObjectsImportConflictError.type property + +Signature: + +```typescript +type: 'conflict'; +``` diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsimporterror.error.md b/docs/development/core/server/kibana-plugin-server.savedobjectsimporterror.error.md new file mode 100644 index 0000000000000..0828ca9e01c34 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsimporterror.error.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsImportError](./kibana-plugin-server.savedobjectsimporterror.md) > [error](./kibana-plugin-server.savedobjectsimporterror.error.md) + +## SavedObjectsImportError.error property + +Signature: + +```typescript +error: SavedObjectsImportConflictError | SavedObjectsImportUnsupportedTypeError | SavedObjectsImportMissingReferencesError | SavedObjectsImportUnknownError; +``` diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsimporterror.id.md b/docs/development/core/server/kibana-plugin-server.savedobjectsimporterror.id.md new file mode 100644 index 0000000000000..0791d668f4626 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsimporterror.id.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsImportError](./kibana-plugin-server.savedobjectsimporterror.md) > [id](./kibana-plugin-server.savedobjectsimporterror.id.md) + +## SavedObjectsImportError.id property + +Signature: + +```typescript +id: string; +``` diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsimporterror.md b/docs/development/core/server/kibana-plugin-server.savedobjectsimporterror.md new file mode 100644 index 0000000000000..0d734c21c3151 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsimporterror.md @@ -0,0 +1,23 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsImportError](./kibana-plugin-server.savedobjectsimporterror.md) + +## SavedObjectsImportError interface + +Represents a failure to import. + +Signature: + +```typescript +export interface SavedObjectsImportError +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [error](./kibana-plugin-server.savedobjectsimporterror.error.md) | SavedObjectsImportConflictError | SavedObjectsImportUnsupportedTypeError | SavedObjectsImportMissingReferencesError | SavedObjectsImportUnknownError | | +| [id](./kibana-plugin-server.savedobjectsimporterror.id.md) | string | | +| [title](./kibana-plugin-server.savedobjectsimporterror.title.md) | string | | +| [type](./kibana-plugin-server.savedobjectsimporterror.type.md) | string | | + diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsimporterror.title.md b/docs/development/core/server/kibana-plugin-server.savedobjectsimporterror.title.md new file mode 100644 index 0000000000000..bd0beeb4d79dd --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsimporterror.title.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsImportError](./kibana-plugin-server.savedobjectsimporterror.md) > [title](./kibana-plugin-server.savedobjectsimporterror.title.md) + +## SavedObjectsImportError.title property + +Signature: + +```typescript +title?: string; +``` diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsimporterror.type.md b/docs/development/core/server/kibana-plugin-server.savedobjectsimporterror.type.md new file mode 100644 index 0000000000000..0b48cc4bbaecf --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsimporterror.type.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsImportError](./kibana-plugin-server.savedobjectsimporterror.md) > [type](./kibana-plugin-server.savedobjectsimporterror.type.md) + +## SavedObjectsImportError.type property + +Signature: + +```typescript +type: string; +``` diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsimportmissingreferenceserror.blocking.md b/docs/development/core/server/kibana-plugin-server.savedobjectsimportmissingreferenceserror.blocking.md new file mode 100644 index 0000000000000..bbbd499ea5844 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsimportmissingreferenceserror.blocking.md @@ -0,0 +1,14 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsImportMissingReferencesError](./kibana-plugin-server.savedobjectsimportmissingreferenceserror.md) > [blocking](./kibana-plugin-server.savedobjectsimportmissingreferenceserror.blocking.md) + +## SavedObjectsImportMissingReferencesError.blocking property + +Signature: + +```typescript +blocking: Array<{ + type: string; + id: string; + }>; +``` diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsimportmissingreferenceserror.md b/docs/development/core/server/kibana-plugin-server.savedobjectsimportmissingreferenceserror.md new file mode 100644 index 0000000000000..fb4e997fe17ba --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsimportmissingreferenceserror.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsImportMissingReferencesError](./kibana-plugin-server.savedobjectsimportmissingreferenceserror.md) + +## SavedObjectsImportMissingReferencesError interface + +Represents a failure to import due to missing references. + +Signature: + +```typescript +export interface SavedObjectsImportMissingReferencesError +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [blocking](./kibana-plugin-server.savedobjectsimportmissingreferenceserror.blocking.md) | Array<{
type: string;
id: string;
}> | | +| [references](./kibana-plugin-server.savedobjectsimportmissingreferenceserror.references.md) | Array<{
type: string;
id: string;
}> | | +| [type](./kibana-plugin-server.savedobjectsimportmissingreferenceserror.type.md) | 'missing_references' | | + diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsimportmissingreferenceserror.references.md b/docs/development/core/server/kibana-plugin-server.savedobjectsimportmissingreferenceserror.references.md new file mode 100644 index 0000000000000..593d2b48d456c --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsimportmissingreferenceserror.references.md @@ -0,0 +1,14 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsImportMissingReferencesError](./kibana-plugin-server.savedobjectsimportmissingreferenceserror.md) > [references](./kibana-plugin-server.savedobjectsimportmissingreferenceserror.references.md) + +## SavedObjectsImportMissingReferencesError.references property + +Signature: + +```typescript +references: Array<{ + type: string; + id: string; + }>; +``` diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsimportmissingreferenceserror.type.md b/docs/development/core/server/kibana-plugin-server.savedobjectsimportmissingreferenceserror.type.md new file mode 100644 index 0000000000000..3e6e80f77c447 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsimportmissingreferenceserror.type.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsImportMissingReferencesError](./kibana-plugin-server.savedobjectsimportmissingreferenceserror.md) > [type](./kibana-plugin-server.savedobjectsimportmissingreferenceserror.type.md) + +## SavedObjectsImportMissingReferencesError.type property + +Signature: + +```typescript +type: 'missing_references'; +``` diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsimportoptions.md b/docs/development/core/server/kibana-plugin-server.savedobjectsimportoptions.md new file mode 100644 index 0000000000000..a1ea33e11b14f --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsimportoptions.md @@ -0,0 +1,25 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsImportOptions](./kibana-plugin-server.savedobjectsimportoptions.md) + +## SavedObjectsImportOptions interface + +Options to control the import operation. + +Signature: + +```typescript +export interface SavedObjectsImportOptions +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [namespace](./kibana-plugin-server.savedobjectsimportoptions.namespace.md) | string | | +| [objectLimit](./kibana-plugin-server.savedobjectsimportoptions.objectlimit.md) | number | | +| [overwrite](./kibana-plugin-server.savedobjectsimportoptions.overwrite.md) | boolean | | +| [readStream](./kibana-plugin-server.savedobjectsimportoptions.readstream.md) | Readable | | +| [savedObjectsClient](./kibana-plugin-server.savedobjectsimportoptions.savedobjectsclient.md) | SavedObjectsClientContract | | +| [supportedTypes](./kibana-plugin-server.savedobjectsimportoptions.supportedtypes.md) | string[] | | + diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsimportoptions.namespace.md b/docs/development/core/server/kibana-plugin-server.savedobjectsimportoptions.namespace.md new file mode 100644 index 0000000000000..600a4dc1176f3 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsimportoptions.namespace.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsImportOptions](./kibana-plugin-server.savedobjectsimportoptions.md) > [namespace](./kibana-plugin-server.savedobjectsimportoptions.namespace.md) + +## SavedObjectsImportOptions.namespace property + +Signature: + +```typescript +namespace?: string; +``` diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsimportoptions.objectlimit.md b/docs/development/core/server/kibana-plugin-server.savedobjectsimportoptions.objectlimit.md new file mode 100644 index 0000000000000..bb040a560b89b --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsimportoptions.objectlimit.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsImportOptions](./kibana-plugin-server.savedobjectsimportoptions.md) > [objectLimit](./kibana-plugin-server.savedobjectsimportoptions.objectlimit.md) + +## SavedObjectsImportOptions.objectLimit property + +Signature: + +```typescript +objectLimit: number; +``` diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsimportoptions.overwrite.md b/docs/development/core/server/kibana-plugin-server.savedobjectsimportoptions.overwrite.md new file mode 100644 index 0000000000000..4586a93568588 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsimportoptions.overwrite.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsImportOptions](./kibana-plugin-server.savedobjectsimportoptions.md) > [overwrite](./kibana-plugin-server.savedobjectsimportoptions.overwrite.md) + +## SavedObjectsImportOptions.overwrite property + +Signature: + +```typescript +overwrite: boolean; +``` diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsimportoptions.readstream.md b/docs/development/core/server/kibana-plugin-server.savedobjectsimportoptions.readstream.md new file mode 100644 index 0000000000000..4b54f931797cf --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsimportoptions.readstream.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsImportOptions](./kibana-plugin-server.savedobjectsimportoptions.md) > [readStream](./kibana-plugin-server.savedobjectsimportoptions.readstream.md) + +## SavedObjectsImportOptions.readStream property + +Signature: + +```typescript +readStream: Readable; +``` diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsimportoptions.savedobjectsclient.md b/docs/development/core/server/kibana-plugin-server.savedobjectsimportoptions.savedobjectsclient.md new file mode 100644 index 0000000000000..f0d439aedc005 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsimportoptions.savedobjectsclient.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsImportOptions](./kibana-plugin-server.savedobjectsimportoptions.md) > [savedObjectsClient](./kibana-plugin-server.savedobjectsimportoptions.savedobjectsclient.md) + +## SavedObjectsImportOptions.savedObjectsClient property + +Signature: + +```typescript +savedObjectsClient: SavedObjectsClientContract; +``` diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsimportoptions.supportedtypes.md b/docs/development/core/server/kibana-plugin-server.savedobjectsimportoptions.supportedtypes.md new file mode 100644 index 0000000000000..0359c53d8fcf1 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsimportoptions.supportedtypes.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsImportOptions](./kibana-plugin-server.savedobjectsimportoptions.md) > [supportedTypes](./kibana-plugin-server.savedobjectsimportoptions.supportedtypes.md) + +## SavedObjectsImportOptions.supportedTypes property + +Signature: + +```typescript +supportedTypes: string[]; +``` diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsimportresponse.errors.md b/docs/development/core/server/kibana-plugin-server.savedobjectsimportresponse.errors.md new file mode 100644 index 0000000000000..c59390c6d45e0 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsimportresponse.errors.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsImportResponse](./kibana-plugin-server.savedobjectsimportresponse.md) > [errors](./kibana-plugin-server.savedobjectsimportresponse.errors.md) + +## SavedObjectsImportResponse.errors property + +Signature: + +```typescript +errors?: SavedObjectsImportError[]; +``` diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsimportresponse.md b/docs/development/core/server/kibana-plugin-server.savedobjectsimportresponse.md new file mode 100644 index 0000000000000..23f6526dc6367 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsimportresponse.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsImportResponse](./kibana-plugin-server.savedobjectsimportresponse.md) + +## SavedObjectsImportResponse interface + +The response describing the result of an import. + +Signature: + +```typescript +export interface SavedObjectsImportResponse +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [errors](./kibana-plugin-server.savedobjectsimportresponse.errors.md) | SavedObjectsImportError[] | | +| [success](./kibana-plugin-server.savedobjectsimportresponse.success.md) | boolean | | +| [successCount](./kibana-plugin-server.savedobjectsimportresponse.successcount.md) | number | | + diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsimportresponse.success.md b/docs/development/core/server/kibana-plugin-server.savedobjectsimportresponse.success.md new file mode 100644 index 0000000000000..5fd76959c556c --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsimportresponse.success.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsImportResponse](./kibana-plugin-server.savedobjectsimportresponse.md) > [success](./kibana-plugin-server.savedobjectsimportresponse.success.md) + +## SavedObjectsImportResponse.success property + +Signature: + +```typescript +success: boolean; +``` diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsimportresponse.successcount.md b/docs/development/core/server/kibana-plugin-server.savedobjectsimportresponse.successcount.md new file mode 100644 index 0000000000000..4b49f57e8367d --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsimportresponse.successcount.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsImportResponse](./kibana-plugin-server.savedobjectsimportresponse.md) > [successCount](./kibana-plugin-server.savedobjectsimportresponse.successcount.md) + +## SavedObjectsImportResponse.successCount property + +Signature: + +```typescript +successCount: number; +``` diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsimportretry.id.md b/docs/development/core/server/kibana-plugin-server.savedobjectsimportretry.id.md new file mode 100644 index 0000000000000..568185b2438b7 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsimportretry.id.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsImportRetry](./kibana-plugin-server.savedobjectsimportretry.md) > [id](./kibana-plugin-server.savedobjectsimportretry.id.md) + +## SavedObjectsImportRetry.id property + +Signature: + +```typescript +id: string; +``` diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsimportretry.md b/docs/development/core/server/kibana-plugin-server.savedobjectsimportretry.md new file mode 100644 index 0000000000000..dc842afbf9f29 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsimportretry.md @@ -0,0 +1,23 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsImportRetry](./kibana-plugin-server.savedobjectsimportretry.md) + +## SavedObjectsImportRetry interface + +Describes a retry operation for importing a saved object. + +Signature: + +```typescript +export interface SavedObjectsImportRetry +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [id](./kibana-plugin-server.savedobjectsimportretry.id.md) | string | | +| [overwrite](./kibana-plugin-server.savedobjectsimportretry.overwrite.md) | boolean | | +| [replaceReferences](./kibana-plugin-server.savedobjectsimportretry.replacereferences.md) | Array<{
type: string;
from: string;
to: string;
}> | | +| [type](./kibana-plugin-server.savedobjectsimportretry.type.md) | string | | + diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsimportretry.overwrite.md b/docs/development/core/server/kibana-plugin-server.savedobjectsimportretry.overwrite.md new file mode 100644 index 0000000000000..36a31e836aebc --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsimportretry.overwrite.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsImportRetry](./kibana-plugin-server.savedobjectsimportretry.md) > [overwrite](./kibana-plugin-server.savedobjectsimportretry.overwrite.md) + +## SavedObjectsImportRetry.overwrite property + +Signature: + +```typescript +overwrite: boolean; +``` diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsimportretry.replacereferences.md b/docs/development/core/server/kibana-plugin-server.savedobjectsimportretry.replacereferences.md new file mode 100644 index 0000000000000..c3439bb554e13 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsimportretry.replacereferences.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsImportRetry](./kibana-plugin-server.savedobjectsimportretry.md) > [replaceReferences](./kibana-plugin-server.savedobjectsimportretry.replacereferences.md) + +## SavedObjectsImportRetry.replaceReferences property + +Signature: + +```typescript +replaceReferences: Array<{ + type: string; + from: string; + to: string; + }>; +``` diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsimportretry.type.md b/docs/development/core/server/kibana-plugin-server.savedobjectsimportretry.type.md new file mode 100644 index 0000000000000..8f0408dcbc11e --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsimportretry.type.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsImportRetry](./kibana-plugin-server.savedobjectsimportretry.md) > [type](./kibana-plugin-server.savedobjectsimportretry.type.md) + +## SavedObjectsImportRetry.type property + +Signature: + +```typescript +type: string; +``` diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsimportunknownerror.md b/docs/development/core/server/kibana-plugin-server.savedobjectsimportunknownerror.md new file mode 100644 index 0000000000000..913038c5bc67d --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsimportunknownerror.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsImportUnknownError](./kibana-plugin-server.savedobjectsimportunknownerror.md) + +## SavedObjectsImportUnknownError interface + +Represents a failure to import due to an unknown reason. + +Signature: + +```typescript +export interface SavedObjectsImportUnknownError +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [message](./kibana-plugin-server.savedobjectsimportunknownerror.message.md) | string | | +| [statusCode](./kibana-plugin-server.savedobjectsimportunknownerror.statuscode.md) | number | | +| [type](./kibana-plugin-server.savedobjectsimportunknownerror.type.md) | 'unknown' | | + diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsimportunknownerror.message.md b/docs/development/core/server/kibana-plugin-server.savedobjectsimportunknownerror.message.md new file mode 100644 index 0000000000000..96b8b98bf6a9f --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsimportunknownerror.message.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsImportUnknownError](./kibana-plugin-server.savedobjectsimportunknownerror.md) > [message](./kibana-plugin-server.savedobjectsimportunknownerror.message.md) + +## SavedObjectsImportUnknownError.message property + +Signature: + +```typescript +message: string; +``` diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsimportunknownerror.statuscode.md b/docs/development/core/server/kibana-plugin-server.savedobjectsimportunknownerror.statuscode.md new file mode 100644 index 0000000000000..9cdef84ff4ea7 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsimportunknownerror.statuscode.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsImportUnknownError](./kibana-plugin-server.savedobjectsimportunknownerror.md) > [statusCode](./kibana-plugin-server.savedobjectsimportunknownerror.statuscode.md) + +## SavedObjectsImportUnknownError.statusCode property + +Signature: + +```typescript +statusCode: number; +``` diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsimportunknownerror.type.md b/docs/development/core/server/kibana-plugin-server.savedobjectsimportunknownerror.type.md new file mode 100644 index 0000000000000..cf31166157ab7 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsimportunknownerror.type.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsImportUnknownError](./kibana-plugin-server.savedobjectsimportunknownerror.md) > [type](./kibana-plugin-server.savedobjectsimportunknownerror.type.md) + +## SavedObjectsImportUnknownError.type property + +Signature: + +```typescript +type: 'unknown'; +``` diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsimportunsupportedtypeerror.md b/docs/development/core/server/kibana-plugin-server.savedobjectsimportunsupportedtypeerror.md new file mode 100644 index 0000000000000..cc775b20bb8f2 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsimportunsupportedtypeerror.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsImportUnsupportedTypeError](./kibana-plugin-server.savedobjectsimportunsupportedtypeerror.md) + +## SavedObjectsImportUnsupportedTypeError interface + +Represents a failure to import due to having an unsupported saved object type. + +Signature: + +```typescript +export interface SavedObjectsImportUnsupportedTypeError +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [type](./kibana-plugin-server.savedobjectsimportunsupportedtypeerror.type.md) | 'unsupported_type' | | + diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsimportunsupportedtypeerror.type.md b/docs/development/core/server/kibana-plugin-server.savedobjectsimportunsupportedtypeerror.type.md new file mode 100644 index 0000000000000..ae69911020c18 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsimportunsupportedtypeerror.type.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsImportUnsupportedTypeError](./kibana-plugin-server.savedobjectsimportunsupportedtypeerror.md) > [type](./kibana-plugin-server.savedobjectsimportunsupportedtypeerror.type.md) + +## SavedObjectsImportUnsupportedTypeError.type property + +Signature: + +```typescript +type: 'unsupported_type'; +``` diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsresolveimporterrorsoptions.md b/docs/development/core/server/kibana-plugin-server.savedobjectsresolveimporterrorsoptions.md new file mode 100644 index 0000000000000..e3542714d96bb --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsresolveimporterrorsoptions.md @@ -0,0 +1,25 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsResolveImportErrorsOptions](./kibana-plugin-server.savedobjectsresolveimporterrorsoptions.md) + +## SavedObjectsResolveImportErrorsOptions interface + +Options to control the "resolve import" operation. + +Signature: + +```typescript +export interface SavedObjectsResolveImportErrorsOptions +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [namespace](./kibana-plugin-server.savedobjectsresolveimporterrorsoptions.namespace.md) | string | | +| [objectLimit](./kibana-plugin-server.savedobjectsresolveimporterrorsoptions.objectlimit.md) | number | | +| [readStream](./kibana-plugin-server.savedobjectsresolveimporterrorsoptions.readstream.md) | Readable | | +| [retries](./kibana-plugin-server.savedobjectsresolveimporterrorsoptions.retries.md) | SavedObjectsImportRetry[] | | +| [savedObjectsClient](./kibana-plugin-server.savedobjectsresolveimporterrorsoptions.savedobjectsclient.md) | SavedObjectsClientContract | | +| [supportedTypes](./kibana-plugin-server.savedobjectsresolveimporterrorsoptions.supportedtypes.md) | string[] | | + diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsresolveimporterrorsoptions.namespace.md b/docs/development/core/server/kibana-plugin-server.savedobjectsresolveimporterrorsoptions.namespace.md new file mode 100644 index 0000000000000..6175e75a4bbec --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsresolveimporterrorsoptions.namespace.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsResolveImportErrorsOptions](./kibana-plugin-server.savedobjectsresolveimporterrorsoptions.md) > [namespace](./kibana-plugin-server.savedobjectsresolveimporterrorsoptions.namespace.md) + +## SavedObjectsResolveImportErrorsOptions.namespace property + +Signature: + +```typescript +namespace?: string; +``` diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsresolveimporterrorsoptions.objectlimit.md b/docs/development/core/server/kibana-plugin-server.savedobjectsresolveimporterrorsoptions.objectlimit.md new file mode 100644 index 0000000000000..d5616851e1386 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsresolveimporterrorsoptions.objectlimit.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsResolveImportErrorsOptions](./kibana-plugin-server.savedobjectsresolveimporterrorsoptions.md) > [objectLimit](./kibana-plugin-server.savedobjectsresolveimporterrorsoptions.objectlimit.md) + +## SavedObjectsResolveImportErrorsOptions.objectLimit property + +Signature: + +```typescript +objectLimit: number; +``` diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsresolveimporterrorsoptions.readstream.md b/docs/development/core/server/kibana-plugin-server.savedobjectsresolveimporterrorsoptions.readstream.md new file mode 100644 index 0000000000000..e4b5d92d7b05a --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsresolveimporterrorsoptions.readstream.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsResolveImportErrorsOptions](./kibana-plugin-server.savedobjectsresolveimporterrorsoptions.md) > [readStream](./kibana-plugin-server.savedobjectsresolveimporterrorsoptions.readstream.md) + +## SavedObjectsResolveImportErrorsOptions.readStream property + +Signature: + +```typescript +readStream: Readable; +``` diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsresolveimporterrorsoptions.retries.md b/docs/development/core/server/kibana-plugin-server.savedobjectsresolveimporterrorsoptions.retries.md new file mode 100644 index 0000000000000..7dc825f762fe9 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsresolveimporterrorsoptions.retries.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsResolveImportErrorsOptions](./kibana-plugin-server.savedobjectsresolveimporterrorsoptions.md) > [retries](./kibana-plugin-server.savedobjectsresolveimporterrorsoptions.retries.md) + +## SavedObjectsResolveImportErrorsOptions.retries property + +Signature: + +```typescript +retries: SavedObjectsImportRetry[]; +``` diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsresolveimporterrorsoptions.savedobjectsclient.md b/docs/development/core/server/kibana-plugin-server.savedobjectsresolveimporterrorsoptions.savedobjectsclient.md new file mode 100644 index 0000000000000..ae5edc98d3a9e --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsresolveimporterrorsoptions.savedobjectsclient.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsResolveImportErrorsOptions](./kibana-plugin-server.savedobjectsresolveimporterrorsoptions.md) > [savedObjectsClient](./kibana-plugin-server.savedobjectsresolveimporterrorsoptions.savedobjectsclient.md) + +## SavedObjectsResolveImportErrorsOptions.savedObjectsClient property + +Signature: + +```typescript +savedObjectsClient: SavedObjectsClientContract; +``` diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsresolveimporterrorsoptions.supportedtypes.md b/docs/development/core/server/kibana-plugin-server.savedobjectsresolveimporterrorsoptions.supportedtypes.md new file mode 100644 index 0000000000000..5a92a8d0a9936 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsresolveimporterrorsoptions.supportedtypes.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsResolveImportErrorsOptions](./kibana-plugin-server.savedobjectsresolveimporterrorsoptions.md) > [supportedTypes](./kibana-plugin-server.savedobjectsresolveimporterrorsoptions.supportedtypes.md) + +## SavedObjectsResolveImportErrorsOptions.supportedTypes property + +Signature: + +```typescript +supportedTypes: string[]; +``` diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsservice.importexport.md b/docs/development/core/server/kibana-plugin-server.savedobjectsservice.importexport.md new file mode 100644 index 0000000000000..f9b4e46712f4a --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsservice.importexport.md @@ -0,0 +1,16 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsService](./kibana-plugin-server.savedobjectsservice.md) > [importExport](./kibana-plugin-server.savedobjectsservice.importexport.md) + +## SavedObjectsService.importExport property + +Signature: + +```typescript +importExport: { + objectLimit: number; + importSavedObjects(options: SavedObjectsImportOptions): Promise; + resolveImportErrors(options: SavedObjectsResolveImportErrorsOptions): Promise; + getSortedObjectsForExport(options: SavedObjectsExportOptions): Promise; + }; +``` diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsservice.md b/docs/development/core/server/kibana-plugin-server.savedobjectsservice.md index ad281002854b3..d9e23e6f15928 100644 --- a/docs/development/core/server/kibana-plugin-server.savedobjectsservice.md +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsservice.md @@ -17,7 +17,9 @@ export interface SavedObjectsService | --- | --- | --- | | [addScopedSavedObjectsClientWrapperFactory](./kibana-plugin-server.savedobjectsservice.addscopedsavedobjectsclientwrapperfactory.md) | ScopedSavedObjectsClientProvider<Request>['addClientWrapperFactory'] | | | [getScopedSavedObjectsClient](./kibana-plugin-server.savedobjectsservice.getscopedsavedobjectsclient.md) | ScopedSavedObjectsClientProvider<Request>['getClient'] | | +| [importExport](./kibana-plugin-server.savedobjectsservice.importexport.md) | {
objectLimit: number;
importSavedObjects(options: SavedObjectsImportOptions): Promise<SavedObjectsImportResponse>;
resolveImportErrors(options: SavedObjectsResolveImportErrorsOptions): Promise<SavedObjectsImportResponse>;
getSortedObjectsForExport(options: SavedObjectsExportOptions): Promise<Readable>;
} | | | [SavedObjectsClient](./kibana-plugin-server.savedobjectsservice.savedobjectsclient.md) | typeof SavedObjectsClient | | +| [schema](./kibana-plugin-server.savedobjectsservice.schema.md) | SavedObjectsSchema | | | [types](./kibana-plugin-server.savedobjectsservice.types.md) | string[] | | ## Methods diff --git a/docs/development/core/server/kibana-plugin-server.savedobjectsservice.schema.md b/docs/development/core/server/kibana-plugin-server.savedobjectsservice.schema.md new file mode 100644 index 0000000000000..be5682e6f034e --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.savedobjectsservice.schema.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SavedObjectsService](./kibana-plugin-server.savedobjectsservice.md) > [schema](./kibana-plugin-server.savedobjectsservice.schema.md) + +## SavedObjectsService.schema property + +Signature: + +```typescript +schema: SavedObjectsSchema; +``` diff --git a/docs/development/core/server/kibana-plugin-server.scopedclusterclient.(constructor).md b/docs/development/core/server/kibana-plugin-server.scopedclusterclient.(constructor).md index 94b49e43a113c..0fea07320b2f9 100644 --- a/docs/development/core/server/kibana-plugin-server.scopedclusterclient.(constructor).md +++ b/docs/development/core/server/kibana-plugin-server.scopedclusterclient.(constructor).md @@ -9,7 +9,7 @@ Constructs a new instance of the `ScopedClusterClient` class Signature: ```typescript -constructor(internalAPICaller: APICaller, scopedAPICaller: APICaller, headers?: Record | undefined); +constructor(internalAPICaller: APICaller, scopedAPICaller: APICaller, headers?: Headers | undefined); ``` ## Parameters @@ -18,5 +18,5 @@ constructor(internalAPICaller: APICaller, scopedAPICaller: APICaller, headers?: | --- | --- | --- | | internalAPICaller | APICaller | | | scopedAPICaller | APICaller | | -| headers | Record<string, string | string[] | undefined> | undefined | | +| headers | Headers | undefined | | diff --git a/docs/development/core/server/kibana-plugin-server.sessionstoragecookieoptions.encryptionkey.md b/docs/development/core/server/kibana-plugin-server.sessionstoragecookieoptions.encryptionkey.md new file mode 100644 index 0000000000000..167ab03d7567f --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.sessionstoragecookieoptions.encryptionkey.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SessionStorageCookieOptions](./kibana-plugin-server.sessionstoragecookieoptions.md) > [encryptionKey](./kibana-plugin-server.sessionstoragecookieoptions.encryptionkey.md) + +## SessionStorageCookieOptions.encryptionKey property + +A key used to encrypt a cookie value. Should be at least 32 characters long. + +Signature: + +```typescript +encryptionKey: string; +``` diff --git a/docs/development/core/server/kibana-plugin-server.sessionstoragecookieoptions.issecure.md b/docs/development/core/server/kibana-plugin-server.sessionstoragecookieoptions.issecure.md new file mode 100644 index 0000000000000..824fc9d136a3f --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.sessionstoragecookieoptions.issecure.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SessionStorageCookieOptions](./kibana-plugin-server.sessionstoragecookieoptions.md) > [isSecure](./kibana-plugin-server.sessionstoragecookieoptions.issecure.md) + +## SessionStorageCookieOptions.isSecure property + +Flag indicating whether the cookie should be sent only via a secure connection. + +Signature: + +```typescript +isSecure: boolean; +``` diff --git a/docs/development/core/server/kibana-plugin-server.sessionstoragecookieoptions.md b/docs/development/core/server/kibana-plugin-server.sessionstoragecookieoptions.md new file mode 100644 index 0000000000000..de412818142f2 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.sessionstoragecookieoptions.md @@ -0,0 +1,23 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SessionStorageCookieOptions](./kibana-plugin-server.sessionstoragecookieoptions.md) + +## SessionStorageCookieOptions interface + +Configuration used to create HTTP session storage based on top of cookie mechanism. + +Signature: + +```typescript +export interface SessionStorageCookieOptions +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [encryptionKey](./kibana-plugin-server.sessionstoragecookieoptions.encryptionkey.md) | string | A key used to encrypt a cookie value. Should be at least 32 characters long. | +| [isSecure](./kibana-plugin-server.sessionstoragecookieoptions.issecure.md) | boolean | Flag indicating whether the cookie should be sent only via a secure connection. | +| [name](./kibana-plugin-server.sessionstoragecookieoptions.name.md) | string | Name of the session cookie. | +| [validate](./kibana-plugin-server.sessionstoragecookieoptions.validate.md) | (sessionValue: T) => boolean | Promise<boolean> | Function called to validate a cookie content. | + diff --git a/docs/development/core/server/kibana-plugin-server.sessionstoragecookieoptions.name.md b/docs/development/core/server/kibana-plugin-server.sessionstoragecookieoptions.name.md new file mode 100644 index 0000000000000..e6bc7ea3fe00f --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.sessionstoragecookieoptions.name.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SessionStorageCookieOptions](./kibana-plugin-server.sessionstoragecookieoptions.md) > [name](./kibana-plugin-server.sessionstoragecookieoptions.name.md) + +## SessionStorageCookieOptions.name property + +Name of the session cookie. + +Signature: + +```typescript +name: string; +``` diff --git a/docs/development/core/server/kibana-plugin-server.sessionstoragecookieoptions.validate.md b/docs/development/core/server/kibana-plugin-server.sessionstoragecookieoptions.validate.md new file mode 100644 index 0000000000000..f3cbfc0d84e18 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.sessionstoragecookieoptions.validate.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [SessionStorageCookieOptions](./kibana-plugin-server.sessionstoragecookieoptions.md) > [validate](./kibana-plugin-server.sessionstoragecookieoptions.validate.md) + +## SessionStorageCookieOptions.validate property + +Function called to validate a cookie content. + +Signature: + +```typescript +validate: (sessionValue: T) => boolean | Promise; +``` diff --git a/docs/images/Dashboard_Resize_Menu.png b/docs/images/Dashboard_Resize_Menu.png index 317ae381db0df..835d23afe40e9 100644 Binary files a/docs/images/Dashboard_Resize_Menu.png and b/docs/images/Dashboard_Resize_Menu.png differ diff --git a/docs/images/Dashboard_add_visualization.png b/docs/images/Dashboard_add_visualization.png index 957e828eff46e..bc705b66e17d1 100644 Binary files a/docs/images/Dashboard_add_visualization.png and b/docs/images/Dashboard_add_visualization.png differ diff --git a/docs/images/Dashboard_example.png b/docs/images/Dashboard_example.png index bf226f48944b0..5d18acb67bef5 100644 Binary files a/docs/images/Dashboard_example.png and b/docs/images/Dashboard_example.png differ diff --git a/docs/images/Dashboard_inspect.png b/docs/images/Dashboard_inspect.png new file mode 100644 index 0000000000000..80edcf3a49ca0 Binary files /dev/null and b/docs/images/Dashboard_inspect.png differ diff --git a/docs/management/advanced-options.asciidoc b/docs/management/advanced-options.asciidoc index cb8777c6fef8a..894f9fd7318a6 100644 --- a/docs/management/advanced-options.asciidoc +++ b/docs/management/advanced-options.asciidoc @@ -43,6 +43,7 @@ removes it from {kib} permanently. `dateFormat:scaled`:: The values that define the format to use to render ordered time-based data. Formatted timestamps must adapt to the interval between measurements. Keys are http://en.wikipedia.org/wiki/ISO_8601#Time_intervals[ISO8601 intervals]. `dateFormat:tz`:: The timezone that Kibana uses. The default value of `Browser` uses the timezone detected by the browser. +`dateNanosFormat`:: The format to use for displaying https://momentjs.com/docs/#/displaying/format/[pretty formatted dates] of {ref}/date_nanos.html[Elasticsearch date_nanos type]. `defaultIndex`:: The index to access if no index is set. The default is `null`. `fields:popularLimit`:: The top N most popular fields to show. `filterEditor:suggestValues`:: Set this property to `false` to prevent the filter editor from suggesting values for fields. diff --git a/docs/maps/images/read-only-badge.png b/docs/maps/images/read-only-badge.png new file mode 100644 index 0000000000000..50289ea80f60c Binary files /dev/null and b/docs/maps/images/read-only-badge.png differ diff --git a/docs/maps/maps-getting-started.asciidoc b/docs/maps/maps-getting-started.asciidoc index 4925ec49897df..cec1938881416 100644 --- a/docs/maps/maps-getting-started.asciidoc +++ b/docs/maps/maps-getting-started.asciidoc @@ -13,6 +13,16 @@ light to dark. [role="screenshot"] image::maps/images/sample_data_web_logs.png[] +[float] +[[maps-read-only-access]] +NOTE: If you have insufficient privileges to create or save maps, a read-only icon +appears in the application header. The buttons to create new maps or edit +existing maps won't be visible. For more information on granting access to +Kibana see <>. + +[role="screenshot"] +image::maps/images/read-only-badge.png[Example of Maps' read only access indicator in Kibana's header] + [float] === Prerequisites Before you start this tutorial, <>. Each diff --git a/docs/settings/code-settings.asciidoc b/docs/settings/code-settings.asciidoc index aed24794158bd..e01858e6230b1 100644 --- a/docs/settings/code-settings.asciidoc +++ b/docs/settings/code-settings.asciidoc @@ -40,3 +40,6 @@ Maximal number of workspaces each language server allows to span. Defaults to `5 `xpack.code.codeNodeUrl`:: URL of the Code node. This config is only needed when multiple Kibana instances are set up as a cluster. Defaults to `` + +`xpack.code.verbose`:: +Set this config to `true` to log all events. Defaults to `false` diff --git a/docs/siem/images/ml-ui.png b/docs/siem/images/ml-ui.png new file mode 100644 index 0000000000000..168ff6363186a Binary files /dev/null and b/docs/siem/images/ml-ui.png differ diff --git a/docs/siem/index.asciidoc b/docs/siem/index.asciidoc index 4606f351d6d6e..6d8aea1f7fe63 100644 --- a/docs/siem/index.asciidoc +++ b/docs/siem/index.asciidoc @@ -52,3 +52,4 @@ SIEM can ingest and normalize events from ECS-compatible data sources. include::siem-ui.asciidoc[] +include::machine-learning.asciidoc[] diff --git a/docs/siem/machine-learning.asciidoc b/docs/siem/machine-learning.asciidoc new file mode 100644 index 0000000000000..dd1016d8550ef --- /dev/null +++ b/docs/siem/machine-learning.asciidoc @@ -0,0 +1,16 @@ +[role="xpack"] +[[machine-learning]] +== Anomaly Detection with Machine Learning + +For *https://www.elastic.co/cloud/elasticsearch-service/signup[Free Trial]* +and *https://www.elastic.co/subscriptions[Platinum License]* deployments, +Machine Learning functionality is available throughout the SIEM app. You can +view the details of detected anomalies within the `Anomalies` table widget +shown on the Hosts, Network and associated Details pages, or even narrow to +the specific daterange of an anomaly from the `Max Anomaly Score` details in +the overview of the Host and IP Details pages. Each of these interfaces also +offer the ability to drag and drop details of the anomaly to Timeline, such +as the `Entity` itself, or any of the associated `Influencers`. + +[role="screenshot"] +image::siem/images/ml-ui.png[Machine Learning - Max Anomaly Score] diff --git a/package.json b/package.json index 2d4259d5d1851..02f476a62810c 100644 --- a/package.json +++ b/package.json @@ -106,7 +106,7 @@ "@babel/register": "7.4.4", "@elastic/charts": "^7.2.1", "@elastic/datemath": "5.0.2", - "@elastic/eui": "13.0.0", + "@elastic/eui": "13.1.1", "@elastic/filesaver": "1.1.2", "@elastic/good": "8.1.1-kibana2", "@elastic/numeral": "2.3.3", diff --git a/packages/kbn-es-query/src/es_query/__tests__/build_es_query.js b/packages/kbn-es-query/src/es_query/__tests__/build_es_query.js index 5c27a204ef263..fde3d063caaa6 100644 --- a/packages/kbn-es-query/src/es_query/__tests__/build_es_query.js +++ b/packages/kbn-es-query/src/es_query/__tests__/build_es_query.js @@ -60,10 +60,10 @@ describe('build query', function () { bool: { must: [ decorateQuery(luceneStringToDsl('bar:baz'), config.queryStringOptions), - { match_all: {} }, ], filter: [ toElasticsearchQuery(fromKueryExpression('extension:jpg'), indexPattern), + { match_all: {} }, ], should: [], must_not: [], @@ -90,9 +90,8 @@ describe('build query', function () { bool: { must: [ decorateQuery(luceneStringToDsl('extension:jpg'), config.queryStringOptions), - { match_all: {} }, ], - filter: [], + filter: [{ match_all: {} }], should: [], must_not: [], } @@ -122,9 +121,11 @@ describe('build query', function () { bool: { must: [ decorateQuery(luceneStringToDsl('@timestamp:"2019-03-23T13:18:00"'), config.queryStringOptions, config.dateFormatTZ), + ], + filter: [ + toElasticsearchQuery(fromKueryExpression('@timestamp:"2019-03-23T13:18:00"'), indexPattern, config), { match_all: {} } ], - filter: [toElasticsearchQuery(fromKueryExpression('@timestamp:"2019-03-23T13:18:00"'), indexPattern, config)], should: [], must_not: [], } diff --git a/packages/kbn-es-query/src/es_query/__tests__/from_filters.js b/packages/kbn-es-query/src/es_query/__tests__/from_filters.js index 53016f33dc6cd..59e5f4d6faf8a 100644 --- a/packages/kbn-es-query/src/es_query/__tests__/from_filters.js +++ b/packages/kbn-es-query/src/es_query/__tests__/from_filters.js @@ -52,7 +52,7 @@ describe('build query', function () { const result = buildQueryFromFilters(filters); - expect(result.must).to.eql(expectedESQueries); + expect(result.filter).to.eql(expectedESQueries); }); it('should place negated filters in the must_not clause', function () { @@ -86,7 +86,7 @@ describe('build query', function () { const result = buildQueryFromFilters(filters); - expect(result.must).to.eql(expectedESQueries); + expect(result.filter).to.eql(expectedESQueries); }); it('should migrate deprecated match syntax', function () { @@ -105,7 +105,7 @@ describe('build query', function () { const result = buildQueryFromFilters(filters); - expect(result.must).to.eql(expectedESQueries); + expect(result.filter).to.eql(expectedESQueries); }); it('should not add query:queryString:options to query_string filters', function () { @@ -119,7 +119,7 @@ describe('build query', function () { const result = buildQueryFromFilters(filters); - expect(result.must).to.eql(expectedESQueries); + expect(result.filter).to.eql(expectedESQueries); }); }); }); diff --git a/packages/kbn-es-query/src/es_query/from_filters.js b/packages/kbn-es-query/src/es_query/from_filters.js index 07f3211b3fc55..b8193b7469a20 100644 --- a/packages/kbn-es-query/src/es_query/from_filters.js +++ b/packages/kbn-es-query/src/es_query/from_filters.js @@ -61,7 +61,8 @@ const cleanFilter = function (filter) { export function buildQueryFromFilters(filters = [], indexPattern, ignoreFilterIfFieldNotInIndex) { return { - must: filters + must: [], + filter: filters .filter(filterNegate(false)) .filter(filter => !ignoreFilterIfFieldNotInIndex || filterMatchesIndex(filter, indexPattern)) .map(translateToQuery) @@ -69,7 +70,6 @@ export function buildQueryFromFilters(filters = [], indexPattern, ignoreFilterIf .map(filter => { return migrateFilter(filter, indexPattern); }), - filter: [], should: [], must_not: filters .filter(filterNegate(true)) diff --git a/src/core/public/i18n/__snapshots__/i18n_service.test.tsx.snap b/src/core/public/i18n/__snapshots__/i18n_service.test.tsx.snap index 7f33c23a8df23..0910635924ea2 100644 --- a/src/core/public/i18n/__snapshots__/i18n_service.test.tsx.snap +++ b/src/core/public/i18n/__snapshots__/i18n_service.test.tsx.snap @@ -46,7 +46,7 @@ exports[`#start() returns \`Context\` component 1`] = ` "euiSelectable.loadingOptions": "Loading options", "euiSelectable.noAvailableOptions": "There aren't any options available", "euiSelectable.noMatchingOptions": [Function], - "euiStat.loadingText": [Function], + "euiStat.loadingText": "Statistic is loading", "euiStep.completeStep": "Step", "euiStep.incompleteStep": "Incomplete Step", "euiStepHorizontal.buttonTitle": [Function], diff --git a/src/core/public/i18n/i18n_service.tsx b/src/core/public/i18n/i18n_service.tsx index 0be47c14e441c..78411a7a418d7 100644 --- a/src/core/public/i18n/i18n_service.tsx +++ b/src/core/public/i18n/i18n_service.tsx @@ -244,9 +244,9 @@ export class I18nService { values={{ searchValue }} /> ), - 'euiStat.loadingText': () => ( - - ), + 'euiStat.loadingText': i18n.translate('core.euiStat.loadingText', { + defaultMessage: 'Statistic is loading', + }), 'euiStep.completeStep': i18n.translate('core.euiStep.completeStep', { defaultMessage: 'Step', description: diff --git a/src/core/server/elasticsearch/cluster_client.ts b/src/core/server/elasticsearch/cluster_client.ts index c8500499d9702..aa40010bf3b1a 100644 --- a/src/core/server/elasticsearch/cluster_client.ts +++ b/src/core/server/elasticsearch/cluster_client.ts @@ -18,10 +18,9 @@ */ import { Client } from 'elasticsearch'; import { get } from 'lodash'; -import { Request } from 'hapi'; import { ElasticsearchErrorHelpers } from './errors'; -import { GetAuthHeaders, isRealRequest } from '../http'; +import { GetAuthHeaders, isRealRequest, LegacyRequest } from '../http'; import { filterHeaders, Headers, KibanaRequest, ensureRawRequest } from '../http/router'; import { Logger } from '../logging'; import { @@ -36,8 +35,6 @@ import { ScopedClusterClient } from './scoped_cluster_client'; * @public */ -export type LegacyRequest = Request; - const noop = () => undefined; /** * The set of options that defines how API call should be made and result be diff --git a/src/core/server/elasticsearch/index.ts b/src/core/server/elasticsearch/index.ts index 1d439dfba49e9..f732f9e39b9e3 100644 --- a/src/core/server/elasticsearch/index.ts +++ b/src/core/server/elasticsearch/index.ts @@ -18,7 +18,7 @@ */ export { ElasticsearchServiceSetup, ElasticsearchService } from './elasticsearch_service'; -export { CallAPIOptions, ClusterClient, FakeRequest, LegacyRequest } from './cluster_client'; +export { CallAPIOptions, ClusterClient, FakeRequest } from './cluster_client'; export { ScopedClusterClient, Headers, APICaller } from './scoped_cluster_client'; export { ElasticsearchClientConfig } from './elasticsearch_client_config'; export { config } from './elasticsearch_config'; diff --git a/src/core/server/http/auth_headers_storage.ts b/src/core/server/http/auth_headers_storage.ts index bc3b55b3718c0..469e194a61fed 100644 --- a/src/core/server/http/auth_headers_storage.ts +++ b/src/core/server/http/auth_headers_storage.ts @@ -16,19 +16,21 @@ * specific language governing permissions and limitations * under the License. */ -import { Request } from 'hapi'; -import { KibanaRequest, ensureRawRequest } from './router'; +import { KibanaRequest, ensureRawRequest, LegacyRequest } from './router'; import { AuthHeaders } from './lifecycle/auth'; /** * Get headers to authenticate a user against Elasticsearch. + * @param request {@link KibanaRequest} - an incoming request. + * @return authentication headers {@link AuthHeaders} for - an incoming request. * @public * */ -export type GetAuthHeaders = (request: KibanaRequest | Request) => AuthHeaders | undefined; +export type GetAuthHeaders = (request: KibanaRequest | LegacyRequest) => AuthHeaders | undefined; +/** @internal */ export class AuthHeadersStorage { - private authHeadersCache = new WeakMap(); - public set = (request: KibanaRequest | Request, headers: AuthHeaders) => { + private authHeadersCache = new WeakMap(); + public set = (request: KibanaRequest | LegacyRequest, headers: AuthHeaders) => { this.authHeadersCache.set(ensureRawRequest(request), headers); }; public get: GetAuthHeaders = request => { diff --git a/src/core/server/http/auth_state_storage.ts b/src/core/server/http/auth_state_storage.ts index 79fd9ed64f3b5..059dc7f380351 100644 --- a/src/core/server/http/auth_state_storage.ts +++ b/src/core/server/http/auth_state_storage.ts @@ -16,22 +16,51 @@ * specific language governing permissions and limitations * under the License. */ -import { Request } from 'hapi'; -import { KibanaRequest, ensureRawRequest } from './router'; +import { ensureRawRequest, KibanaRequest, LegacyRequest } from './router'; +/** + * Status indicating an outcome of the authentication. + * @public + */ export enum AuthStatus { + /** + * `auth` interceptor successfully authenticated a user + */ authenticated = 'authenticated', + /** + * `auth` interceptor failed user authentication + */ unauthenticated = 'unauthenticated', + /** + * `auth` interceptor has not been registered + */ unknown = 'unknown', } +/** + * Get authentication state for a request. Returned by `auth` interceptor. + * @param request {@link KibanaRequest} - an incoming request. + * @public + */ +export type GetAuthState = ( + request: KibanaRequest | LegacyRequest +) => { status: AuthStatus; state: unknown }; + +/** + * Return authentication status for a request. + * @param request {@link KibanaRequest} - an incoming request. + * @public + */ +export type IsAuthenticated = (request: KibanaRequest | LegacyRequest) => boolean; + +/** @internal */ export class AuthStateStorage { - private readonly storage = new WeakMap(); + private readonly storage = new WeakMap(); constructor(private readonly canBeAuthenticated: () => boolean) {} - public set = (request: KibanaRequest | Request, state: unknown) => { + public set = (request: KibanaRequest | LegacyRequest, state: unknown) => { this.storage.set(ensureRawRequest(request), state); }; - public get = (request: KibanaRequest | Request) => { + public get: GetAuthState = request => { const key = ensureRawRequest(request); const state = this.storage.get(key); const status: AuthStatus = this.storage.has(key) @@ -42,7 +71,7 @@ export class AuthStateStorage { return { status, state }; }; - public isAuthenticated = (request: KibanaRequest | Request) => { + public isAuthenticated: IsAuthenticated = request => { return this.get(request).status === AuthStatus.authenticated; }; } diff --git a/src/core/server/http/base_path_proxy_server.ts b/src/core/server/http/base_path_proxy_server.ts index 2335f35ab63c1..ff7fee0198f68 100644 --- a/src/core/server/http/base_path_proxy_server.ts +++ b/src/core/server/http/base_path_proxy_server.ts @@ -18,7 +18,8 @@ */ import { ByteSizeValue } from '@kbn/config-schema'; -import { Server } from 'hapi'; +import { Server, Request } from 'hapi'; +import Url from 'url'; import { Agent as HttpsAgent, ServerOptions as TlsOptions } from 'https'; import { sample } from 'lodash'; import { DevConfig } from '../dev'; @@ -146,6 +147,38 @@ export class BasePathProxyServer { path: `${this.httpConfig.basePath}/{kbnPath*}`, }); + this.server.route({ + handler: { + proxy: { + agent: this.httpsAgent, + passThrough: true, + xforward: true, + mapUri: (request: Request) => ({ + uri: Url.format({ + hostname: request.server.info.host, + port: this.devConfig.basePathProxyTargetPort, + protocol: request.server.info.protocol, + pathname: `${this.httpConfig.basePath}/${request.params.kbnPath}`, + query: request.query, + }), + headers: request.headers, + }), + }, + }, + method: '*', + options: { + pre: [ + // Before we proxy request to a target port we may want to wait until some + // condition is met (e.g. until target listener is ready). + async (request, responseToolkit) => { + await blockUntil(); + return responseToolkit.continue; + }, + ], + }, + path: `/__UNSAFE_bypassBasePath/{kbnPath*}`, + }); + // It may happen that basepath has changed, but user still uses the old one, // so we can try to check if that's the case and just redirect user to the // same URL, but with valid basepath. diff --git a/src/core/server/http/base_path_service.ts b/src/core/server/http/base_path_service.ts index df189d29f2f59..951463a2c9919 100644 --- a/src/core/server/http/base_path_service.ts +++ b/src/core/server/http/base_path_service.ts @@ -16,24 +16,23 @@ * specific language governing permissions and limitations * under the License. */ -import { Request } from 'hapi'; -import { KibanaRequest, ensureRawRequest } from './router'; +import { ensureRawRequest, KibanaRequest, LegacyRequest } from './router'; import { modifyUrl } from '../../utils'; export class BasePath { - private readonly basePathCache = new WeakMap(); + private readonly basePathCache = new WeakMap(); constructor(private readonly serverBasePath?: string) {} - public get = (request: KibanaRequest | Request) => { + public get = (request: KibanaRequest | LegacyRequest) => { const requestScopePath = this.basePathCache.get(ensureRawRequest(request)) || ''; const serverBasePath = this.serverBasePath || ''; return `${serverBasePath}${requestScopePath}`; }; // should work only for KibanaRequest as soon as spaces migrate to NP - public set = (request: KibanaRequest | Request, requestSpecificBasePath: string) => { + public set = (request: KibanaRequest | LegacyRequest, requestSpecificBasePath: string) => { const rawRequest = ensureRawRequest(request); if (this.basePathCache.has(rawRequest)) { diff --git a/src/core/server/http/cookie_session_storage.ts b/src/core/server/http/cookie_session_storage.ts index 7b2569a1c6dd3..8a1b56d87fb4c 100644 --- a/src/core/server/http/cookie_session_storage.ts +++ b/src/core/server/http/cookie_session_storage.ts @@ -24,10 +24,26 @@ import { KibanaRequest, ensureRawRequest } from './router'; import { SessionStorageFactory, SessionStorage } from './session_storage'; import { Logger } from '..'; +/** + * Configuration used to create HTTP session storage based on top of cookie mechanism. + * @public + */ export interface SessionStorageCookieOptions { + /** + * Name of the session cookie. + */ name: string; + /** + * A key used to encrypt a cookie value. Should be at least 32 characters long. + */ encryptionKey: string; + /** + * Function called to validate a cookie content. + */ validate: (sessionValue: T) => boolean | Promise; + /** + * Flag indicating whether the cookie should be sent only via a secure connection. + */ isSecure: boolean; } diff --git a/src/core/server/http/http_server.ts b/src/core/server/http/http_server.ts index c8b4c5c17f05e..d90fb880a581c 100644 --- a/src/core/server/http/http_server.ts +++ b/src/core/server/http/http_server.ts @@ -25,21 +25,98 @@ import { createServer, getListenerOptions, getServerOptions } from './http_tools import { adoptToHapiAuthFormat, AuthenticationHandler } from './lifecycle/auth'; import { adoptToHapiOnPostAuthFormat, OnPostAuthHandler } from './lifecycle/on_post_auth'; import { adoptToHapiOnPreAuthFormat, OnPreAuthHandler } from './lifecycle/on_pre_auth'; -import { Router, KibanaRequest, ResponseHeaders } from './router'; +import { KibanaRequest, LegacyRequest, ResponseHeaders, Router } from './router'; import { SessionStorageCookieOptions, createCookieSessionStorageFactory, } from './cookie_session_storage'; import { SessionStorageFactory } from './session_storage'; -import { AuthStateStorage } from './auth_state_storage'; -import { AuthHeadersStorage } from './auth_headers_storage'; +import { AuthStateStorage, GetAuthState, IsAuthenticated } from './auth_state_storage'; +import { AuthHeadersStorage, GetAuthHeaders } from './auth_headers_storage'; import { BasePath } from './base_path_service'; +/** + * Kibana HTTP Service provides own abstraction for work with HTTP stack. + * Plugins don't have direct access to `hapi` server and its primitives anymore. Moreover, + * plugins shouldn't rely on the fact that HTTP Service uses one or another library under the hood. + * This gives the platform flexibility to upgrade or changing our internal HTTP stack without breaking plugins. + * If the HTTP Service lacks functionality you need, we are happy to discuss and support your needs. + * + * @example + * To handle an incoming request in your plugin you should: + * - Create a `Router` instance. Use `plugin-id` as a prefix path segment for your routes. + * ```ts + * import { Router } from 'src/core/server'; + * const router = new Router('my-app'); + * ``` + * + * - Use `@kbn/config-schema` package to create a schema to validate the request `params`, `query`, and `body`. Every incoming request will be validated against the created schema. If validation failed, the request is rejected with `400` status and `Bad request` error without calling the route's handler. + * To opt out of validating the request, specify `false`. + * ```ts + * import { schema, TypeOf } from '@kbn/config-schema'; + * const validate = { + * params: schema.object({ + * id: schema.string(), + * }), + * }; + * ``` + * + * - Declare a function to respond to incoming request. + * The function will receive `request` object containing request details: url, headers, matched route, as well as validated `params`, `query`, `body`. + * And `response` object instructing HTTP server to create HTTP response with information sent back to the client as the response body, headers, and HTTP status. + * Unlike, `hapi` route handler in the Legacy platform, any exception raised during the handler call will generate `500 Server error` response and log error details for further investigation. See below for returning custom error responses. + * ```ts + * const handler = async (request: KibanaRequest, response: ResponseFactory) => { + * const data = await findObject(request.params.id); + * // creates a command to respond with 'not found' error + * if (!data) return response.notFound(); + * // creates a command to send found data to the client and set response headers + * return response.ok(data, { + * headers: { + * 'content-type': 'application/json' + * } + * }); + * } + * ``` + * + * - Register route handler for GET request to 'my-app/path/{id}' path + * ```ts + * import { schema, TypeOf } from '@kbn/config-schema'; + * import { Router } from 'src/core/server'; + * const router = new Router('my-app'); + * + * const validate = { + * params: schema.object({ + * id: schema.string(), + * }), + * }; + * + * router.get({ + * path: 'path/{id}', + * validate + * }, + * async (request, response) => { + * const data = await findObject(request.params.id); + * if (!data) return response.notFound(); + * return response.ok(data, { + * headers: { + * 'content-type': 'application/json' + * } + * }); + * }); + * ``` + * @public + */ export interface HttpServerSetup { server: Server; + /** + * Add all the routes registered with `router` to HTTP server request listeners. + * @param router {@link Router} - a router with registered route handlers. + */ registerRouter: (router: Router) => void; /** * Creates cookie based session storage factory {@link SessionStorageFactory} + * @param cookieOptions {@link SessionStorageCookieOptions} - options to configure created cookie session storage. */ createCookieSessionStorageFactory: ( cookieOptions: SessionStorageCookieOptions @@ -49,35 +126,53 @@ export interface HttpServerSetup { * A handler should return a state to associate with the incoming request. * The state can be retrieved later via http.auth.get(..) * Only one AuthenticationHandler can be registered. + * @param handler {@link AuthenticationHandler} - function to perform authentication. */ registerAuth: (handler: AuthenticationHandler) => void; /** * To define custom logic to perform for incoming requests. Runs the handler before Auth - * hook performs a check that user has access to requested resources, so it's the only + * interceptor performs a check that user has access to requested resources, so it's the only * place when you can forward a request to another URL right on the server. * Can register any number of registerOnPostAuth, which are called in sequence * (from the first registered to the last). + * @param handler {@link OnPreAuthHandler} - function to call. */ registerOnPreAuth: (handler: OnPreAuthHandler) => void; /** - * To define custom logic to perform for incoming requests. Runs the handler after Auth hook + * To define custom logic to perform for incoming requests. Runs the handler after Auth interceptor * did make sure a user has access to the requested resource. * The auth state is available at stage via http.auth.get(..) * Can register any number of registerOnPreAuth, which are called in sequence * (from the first registered to the last). + * @param handler {@link OnPostAuthHandler} - function to call. */ registerOnPostAuth: (handler: OnPostAuthHandler) => void; basePath: { - get: (request: KibanaRequest | Request) => string; - set: (request: KibanaRequest | Request, basePath: string) => void; + /** + * returns `basePath` value, specific for an incoming request. + */ + get: (request: KibanaRequest | LegacyRequest) => string; + /** + * sets `basePath` value, specific for an incoming request. + */ + set: (request: KibanaRequest | LegacyRequest, basePath: string) => void; + /** + * returns a new `basePath` value, prefixed with passed `url`. + */ prepend: (url: string) => string; + /** + * returns a new `basePath` value, cleaned up from passed `url`. + */ remove: (url: string) => string; }; auth: { - get: AuthStateStorage['get']; - isAuthenticated: AuthStateStorage['isAuthenticated']; - getAuthHeaders: AuthHeadersStorage['get']; + get: GetAuthState; + isAuthenticated: IsAuthenticated; + getAuthHeaders: GetAuthHeaders; }; + /** + * Flag showing whether a server was configured to use TLS connection. + */ isTlsEnabled: boolean; } diff --git a/src/core/server/http/http_service.mock.ts b/src/core/server/http/http_service.mock.ts index 02103fc4acc84..b4e509c8a05a5 100644 --- a/src/core/server/http/http_service.mock.ts +++ b/src/core/server/http/http_service.mock.ts @@ -19,7 +19,6 @@ import { Server } from 'hapi'; import { HttpService } from './http_service'; -import { HttpServerSetup } from './http_server'; import { HttpServiceSetup } from './http_service'; import { OnPreAuthToolkit } from './lifecycle/on_pre_auth'; import { AuthToolkit } from './lifecycle/auth'; @@ -52,10 +51,8 @@ const createSetupContractMock = () => { isAuthenticated: jest.fn(), getAuthHeaders: jest.fn(), }, - createNewServer: jest.fn(), isTlsEnabled: false, }; - setupContract.createNewServer.mockResolvedValue({} as HttpServerSetup); setupContract.createCookieSessionStorageFactory.mockResolvedValue( sessionStorageMock.createFactory() ); diff --git a/src/core/server/http/http_service.test.ts b/src/core/server/http/http_service.test.ts index f003ba1314434..cde06dc31802f 100644 --- a/src/core/server/http/http_service.test.ts +++ b/src/core/server/http/http_service.test.ts @@ -133,46 +133,6 @@ test('spins up notReady server until started if configured with `autoListen:true expect(notReadyHapiServer.stop).toBeCalledTimes(1); }); -// this is an integration test! -test('creates and sets up second http server', async () => { - const configService = createConfigService({ - host: 'localhost', - port: 1234, - }); - const { HttpServer } = jest.requireActual('./http_server'); - - mockHttpServer.mockImplementation((...args) => new HttpServer(...args)); - - const service = new HttpService({ configService, env, logger }); - const serverSetup = await service.setup(); - const cfg = { port: 2345 }; - await serverSetup.createNewServer(cfg); - const server = await service.start(); - expect(server.isListening()).toBeTruthy(); - expect(server.isListening(cfg.port)).toBeTruthy(); - - try { - await serverSetup.createNewServer(cfg); - } catch (err) { - expect(err.message).toBe('port 2345 is already in use'); - } - - try { - await serverSetup.createNewServer({ port: 1234 }); - } catch (err) { - expect(err.message).toBe('port 1234 is already in use'); - } - - try { - await serverSetup.createNewServer({ host: 'example.org' }); - } catch (err) { - expect(err.message).toBe('port must be defined'); - } - await service.stop(); - expect(server.isListening()).toBeFalsy(); - expect(server.isListening(cfg.port)).toBeFalsy(); -}); - test('logs error if already set up', async () => { const configService = createConfigService(); @@ -273,8 +233,7 @@ test('returns http server contract on setup', async () => { })); const service = new HttpService({ configService, env, logger }); - const { createNewServer, ...setupHttpServer } = await service.setup(); - expect(createNewServer).toBeDefined(); + const setupHttpServer = await service.setup(); expect(setupHttpServer).toEqual(httpServer); }); diff --git a/src/core/server/http/http_service.ts b/src/core/server/http/http_service.ts index b06c690cf2621..e69906d512bac 100644 --- a/src/core/server/http/http_service.ts +++ b/src/core/server/http/http_service.ts @@ -25,14 +25,12 @@ import { LoggerFactory } from '../logging'; import { CoreService } from '../../types'; import { Logger } from '../logging'; import { CoreContext } from '../core_context'; -import { HttpConfig, HttpConfigType, config as httpConfig } from './http_config'; +import { HttpConfig, HttpConfigType } from './http_config'; import { HttpServer, HttpServerSetup } from './http_server'; import { HttpsRedirectServer } from './https_redirect_server'; /** @public */ -export interface HttpServiceSetup extends HttpServerSetup { - createNewServer: (cfg: Partial) => Promise; -} +export type HttpServiceSetup = HttpServerSetup; /** @public */ export interface HttpServiceStart { /** Indicates if http server is listening on a given port */ @@ -42,7 +40,6 @@ export interface HttpServiceStart { /** @internal */ export class HttpService implements CoreService { private readonly httpServer: HttpServer; - private readonly secondaryServers: Map = new Map(); private readonly httpsRedirectServer: HttpsRedirectServer; private readonly config$: Observable; private configSubscription?: Subscription; @@ -77,17 +74,11 @@ export class HttpService implements CoreService server.start())); } return { - isListening: (port: number = 0) => { - const server = this.secondaryServers.get(port); - if (server) return server.isListening(); - return this.httpServer.isListening(); - }, + isListening: () => this.httpServer.isListening(), }; } @@ -129,32 +115,6 @@ export class HttpService implements CoreService) { - const { port } = cfg; - const config = await this.config$.pipe(first()).toPromise(); - - if (!port) { - throw new Error('port must be defined'); - } - - // verify that main server and none of the secondary servers are already using this port - if (this.secondaryServers.has(port) || config.port === port) { - throw new Error(`port ${port} is already in use`); - } - - for (const [key, val] of Object.entries(cfg)) { - httpConfig.schema.validateKey(key, val); - } - - const baseConfig = await this.config$.pipe(first()).toPromise(); - const finalConfig = { ...baseConfig, ...cfg }; - - const httpServer = new HttpServer(this.logger, `secondary server:${port}`); - const httpSetup = await httpServer.setup(finalConfig); - this.secondaryServers.set(port, httpServer); - return httpSetup; - } - public async stop() { if (this.configSubscription === undefined) { return; @@ -168,8 +128,6 @@ export class HttpService implements CoreService s.stop())); - this.secondaryServers.clear(); } private async runNotReadyServer(config: HttpConfig) { @@ -177,7 +135,7 @@ export class HttpService implements CoreService; -export type ResponseHeaders = Record; +/** + * Creates a Union type of all known keys of a given interface. + * @example + * ```ts + * interface Person { + * name: string; + * age: number; + * [attributes: string]: string | number; + * } + * type PersonKnownKeys = KnownKeys; // "age" | "name" + * ``` + */ +type KnownKeys = { + [K in keyof T]: string extends K ? never : number extends K ? never : K; +} extends { [_ in keyof T]: infer U } + ? U + : never; + +/** + * Set of well-known HTTP headers. + * @public + */ +export type KnownHeaders = KnownKeys; + +/** + * Http request headers to read. + * @public + */ +export type Headers = { [header in KnownHeaders]?: string | string[] | undefined } & { + [header: string]: string | string[] | undefined; +}; + +/** + * Http response headers to set. + * @public + */ +export type ResponseHeaders = { [header in KnownHeaders]?: string | string[] } & { + [header: string]: string | string[]; +}; const normalizeHeaderField = (field: string) => field.trim().toLowerCase(); diff --git a/src/core/server/http/router/index.ts b/src/core/server/http/router/index.ts index eefa74cee0802..f9009949825ba 100644 --- a/src/core/server/http/router/index.ts +++ b/src/core/server/http/router/index.ts @@ -17,8 +17,23 @@ * under the License. */ -export { Headers, filterHeaders, ResponseHeaders } from './headers'; -export { Router } from './router'; -export { KibanaRequest, KibanaRequestRoute, ensureRawRequest, isRealRequest } from './request'; -export { RouteMethod, RouteConfigOptions } from './route'; -export { ResponseError, ResponseErrorMeta } from './response'; +export { Headers, filterHeaders, ResponseHeaders, KnownHeaders } from './headers'; +export { Router, RequestHandler } from './router'; +export { + KibanaRequest, + KibanaRequestRoute, + isRealRequest, + LegacyRequest, + ensureRawRequest, +} from './request'; +export { RouteMethod, RouteConfig, RouteConfigOptions } from './route'; +export { + CustomHttpResponseOptions, + HttpResponseOptions, + HttpResponsePayload, + RedirectResponseOptions, + ResponseError, + ResponseErrorMeta, + kibanaResponseFactory, + KibanaResponseFactory, +} from './response'; diff --git a/src/core/server/http/router/request.ts b/src/core/server/http/router/request.ts index da10a6500ccc8..4eac2e98317fc 100644 --- a/src/core/server/http/router/request.ts +++ b/src/core/server/http/router/request.ts @@ -38,16 +38,22 @@ export interface KibanaRequestRoute { options: Required; } +/** + * @deprecated + * `hapi` request object, supported during migration process only for backward compatibility. + * @public + */ +export interface LegacyRequest extends Request {} // eslint-disable-line @typescript-eslint/no-empty-interface + /** * Kibana specific abstraction for an incoming request. * @public - * */ + */ export class KibanaRequest { /** * Factory for creating requests. Validates the request before creating an * instance of a KibanaRequest. * @internal - * */ public static from

( req: Request, @@ -68,6 +74,7 @@ export class KibanaRequest { * Validates the different parts of a request based on the schemas defined for * the route. Builds up the actual params, query and body object that will be * received in the route handler. + * @internal */ private static validate

( req: Request, @@ -102,8 +109,9 @@ export class KibanaRequest { return { query, params, body }; } - + /** a WHATWG URL standard object. */ public readonly url: Url; + /** matched route details */ public readonly route: RecursiveReadonly; /** * Readonly copy of incoming request headers. @@ -153,14 +161,14 @@ export class KibanaRequest { * Returns underlying Hapi Request * @internal */ -export const ensureRawRequest = (request: KibanaRequest | Request) => +export const ensureRawRequest = (request: KibanaRequest | LegacyRequest) => isKibanaRequest(request) ? request[requestSymbol] : request; function isKibanaRequest(request: unknown): request is KibanaRequest { return request instanceof KibanaRequest; } -function isRequest(request: any): request is Request { +function isRequest(request: any): request is LegacyRequest { try { return request.raw.req && typeof request.raw.req === 'object'; } catch { @@ -172,6 +180,6 @@ function isRequest(request: any): request is Request { * Checks if an incoming request either KibanaRequest or Legacy.Request * @internal */ -export function isRealRequest(request: unknown): request is KibanaRequest | Request { +export function isRealRequest(request: unknown): request is KibanaRequest | LegacyRequest { return isKibanaRequest(request) || isRequest(request); } diff --git a/src/core/server/http/router/response.ts b/src/core/server/http/router/response.ts index 65db87f8ae8f8..100c4b0d660cd 100644 --- a/src/core/server/http/router/response.ts +++ b/src/core/server/http/router/response.ts @@ -16,8 +16,8 @@ * specific language governing permissions and limitations * under the License. */ -import { IncomingHttpHeaders } from 'http'; import { Stream } from 'stream'; +import { ResponseHeaders } from './headers'; /** * Additional metadata to enhance error output or provide error details. @@ -40,6 +40,10 @@ export type ResponseError = meta?: ResponseErrorMeta; }; +/** + * A response data object, expected to returned as a result of {@link RequestHandler} execution + * @internal + */ export class KibanaResponse { constructor( readonly status: number, @@ -48,51 +52,31 @@ export class KibanaResponse { ) {} } -/** - * Creates a Union type of all known keys of a given interface. - * @example - * ```ts - * interface Person { - * name: string; - * age: number; - * [attributes: string]: string | number; - * } - * type PersonKnownKeys = KnownKeys; // "age" | "name" - * ``` - */ -type KnownKeys = { - [K in keyof T]: string extends K ? never : number extends K ? never : K; -} extends { [_ in keyof T]: infer U } - ? U - : never; - -type KnownHeaders = KnownKeys; /** * HTTP response parameters * @public */ export interface HttpResponseOptions { /** HTTP Headers with additional information about response */ - headers?: { [header in KnownHeaders]?: string | string[] } & { - [header: string]: string | string[]; - }; + headers?: ResponseHeaders; } /** + * Data send to the client as a response payload. * @public */ export type HttpResponsePayload = undefined | string | Record | Buffer | Stream; /** - * HTTP response parameters + * HTTP response parameters for a response with adjustable status code. * @public */ -export interface CustomResponseOptions extends HttpResponseOptions { +export interface CustomHttpResponseOptions extends HttpResponseOptions { statusCode: number; } /** - * HTTP response parameters + * HTTP response parameters for redirection response * @public */ export type RedirectResponseOptions = HttpResponseOptions & { @@ -101,7 +85,99 @@ export type RedirectResponseOptions = HttpResponseOptions & { }; }; -export const responseFactory = { +/** + * Set of helpers used to create `KibanaResponse` to form HTTP response on an incoming request. + * Should be returned as a result of {@link RequestHandler} execution. + * + * @example + * 1. Successful response. Supported types of response body are: + * - `undefined`, no content to send. + * - `string`, send text + * - `JSON`, send JSON object, HTTP server will throw if given object is not valid (has circular references, for example) + * - `Stream` send data stream + * - `Buffer` send binary stream + * ```js + * return response.ok(undefined); + * return response.ok('ack'); + * return response.ok({ id: '1' }); + * return response.ok(Buffer.from(...);); + * + * const stream = new Stream.PassThrough(); + * fs.createReadStream('./file').pipe(stream); + * return res.ok(stream); + * ``` + * HTTP headers are configurable via response factory parameter `options` {@link HttpResponseOptions}. + * + * ```js + * return response.ok({ id: '1' }, { + * headers: { + * 'content-type': 'application/json' + * } + * }); + * ``` + * 2. Redirection response. Redirection URL is configures via 'Location' header. + * ```js + * return response.redirected('The document has moved', { + * headers: { + * location: '/new-url', + * }, + * }); + * ``` + * 3. Error response. You may pass an error message to the client, where error message can be: + * - `string` send message text + * - `Error` send the message text of given Error object. + * - `{ message: string | Error, meta: {data: Record, ...} }` - send message text and attach additional error metadata. + * ```js + * return response.unauthorized('User has no access to the requested resource.', { + * headers: { + * 'WWW-Authenticate': 'challenge', + * } + * }) + * return response.badRequest(); + * return response.badRequest('validation error'); + * + * try { + * // ... + * } catch(error){ + * return response.badRequest(error); + * } + * + * return response.badRequest({ + * message: 'validation error', + * meta: { + * data: { + * requestBody: request.body, + * failedFields: validationResult + * }, + * } + * }); + * + * try { + * // ... + * } catch(error) { + * return response.badRequest({ + * message: error, + * meta: { + * data: { + * requestBody: request.body, + * }, + * } + * }); + * } + * + * ``` + * 4. Custom response. `ResponseFactory` may not cover your use case, so you can use the `custom` function to customize the response. + * ```js + * return response.custom('ok', { + * statusCode: 201, + * headers: { + * location: '/created-url' + * } + * }) + * ``` + * @public + */ +export const kibanaResponseFactory = { // Success /** * The request has succeeded. @@ -131,9 +207,9 @@ export const responseFactory = { /** * Creates a response with defined status code and payload. * @param payload - {@link HttpResponsePayload} payload to send to the client - * @param options - {@link CustomResponseOptions} configures HTTP response parameters. + * @param options - {@link CustomHttpResponseOptions} configures HTTP response parameters. */ - custom: (payload: HttpResponsePayload | ResponseError, options: CustomResponseOptions) => { + custom: (payload: HttpResponsePayload | ResponseError, options: CustomHttpResponseOptions) => { if (!options || !options.statusCode) { throw new Error(`options.statusCode is expected to be set. given options: ${options}`); } @@ -213,4 +289,4 @@ export const responseFactory = { * Creates an object containing request response payload, HTTP headers, error details, and other data transmitted to the client. * @public */ -export type ResponseFactory = typeof responseFactory; +export type KibanaResponseFactory = typeof kibanaResponseFactory; diff --git a/src/core/server/http/router/route.ts b/src/core/server/http/router/route.ts index caf13343ec96c..e805356014829 100644 --- a/src/core/server/http/router/route.ts +++ b/src/core/server/http/router/route.ts @@ -21,18 +21,18 @@ import { ObjectType } from '@kbn/config-schema'; /** * The set of common HTTP methods supported by Kibana routing. * @public - * */ + */ export type RouteMethod = 'get' | 'post' | 'put' | 'delete'; /** - * Route specific configuration. + * Additional route options. * @public - * */ + */ export interface RouteConfigOptions { /** * A flag shows that authentication for a route: - * enabled when true - * disabled when false + * `enabled` when true + * `disabled` when false * * Enabled by default. */ @@ -44,27 +44,58 @@ export interface RouteConfigOptions { tags?: readonly string[]; } +/** + * Route specific configuration. + * @public + */ export interface RouteConfig

{ /** * The endpoint _within_ the router path to register the route. E.g. if the * router is registered at `/elasticsearch` and the route path is `/search`, * the full path for the route is `/elasticsearch/search`. + * Supports: + * - named path segments `path/{name}`. + * - optional path segments `path/{position?}`. + * - multi-segments `path/{coordinates*2}`. + * Segments are accessible within a handler function as `params` property of {@link KibanaRequest} object. + * To have read access to `params` you *must* specify validation schema with {@link RouteConfig.validate}. */ path: string; /** * A schema created with `@kbn/config-schema` that every request will be validated against. - * + * You *must* specify a validation schema to be able to read: + * - url path segments + * - request query + * - request body * To opt out of validating the request, specify `false`. + * @example + * ```ts + * import { schema } from '@kbn/config-schema'; + * router.get({ + * path: 'path/{id}' + * validate: { + * params: schema.object({ + * id: schema.string(), + * }), + * query: schema.object({...}), + * body: schema.object({...}), + * }, + * }) + * ``` */ validate: RouteSchemas | false; + /** + * Additional route options {@link RouteConfigOptions}. + */ options?: RouteConfigOptions; } /** * RouteSchemas contains the schemas for validating the different parts of a * request. + * @public */ export interface RouteSchemas

{ params?: P; diff --git a/src/core/server/http/router/router.ts b/src/core/server/http/router/router.ts index b801aa41754fa..a4d4b62e40203 100644 --- a/src/core/server/http/router/router.ts +++ b/src/core/server/http/router/router.ts @@ -22,25 +22,41 @@ import { Request, ResponseObject, ResponseToolkit } from 'hapi'; import { Logger } from '../../logging'; import { KibanaRequest } from './request'; -import { KibanaResponse, ResponseFactory, responseFactory } from './response'; +import { KibanaResponse, KibanaResponseFactory, kibanaResponseFactory } from './response'; import { RouteConfig, RouteConfigOptions, RouteMethod, RouteSchemas } from './route'; import { HapiResponseAdapter } from './response_adapter'; -export interface RouterRoute { +interface RouterRoute { method: RouteMethod; path: string; options: RouteConfigOptions; handler: (req: Request, responseToolkit: ResponseToolkit, log: Logger) => Promise; } -/** @public */ +/** + * Provides ability to declare a handler function for a particular path and HTTP request method. + * Each route can have only one handler functions, which is executed when the route is matched. + * + * @example + * ```ts + * const router = new Router('my-app'); + * // handler is called when 'my-app/path' resource is requested with `GET` method + * router.get({ path: '/path', validate: false }, (req, res) => res.ok({ content: 'ok' })); + * ``` + * + * @public + * */ export class Router { public routes: Array> = []; - + /** + * @param path - a router path, set as the very first path segment for all registered routes. + */ constructor(readonly path: string) {} /** - * Register a `GET` request with the router + * Register a route handler for `GET` request. + * @param route {@link RouteConfig} - a route configuration. + * @param handler {@link RequestHandler} - a function to call to respond to an incoming request */ public get

( route: RouteConfig, @@ -58,7 +74,9 @@ export class Router { } /** - * Register a `POST` request with the router + * Register a route handler for `POST` request. + * @param route {@link RouteConfig} - a route configuration. + * @param handler {@link RequestHandler} - a function to call to respond to an incoming request */ public post

( route: RouteConfig, @@ -76,7 +94,9 @@ export class Router { } /** - * Register a `PUT` request with the router + * Register a route handler for `PUT` request. + * @param route {@link RouteConfig} - a route configuration. + * @param handler {@link RequestHandler} - a function to call to respond to an incoming request */ public put

( route: RouteConfig, @@ -94,7 +114,9 @@ export class Router { } /** - * Register a `DELETE` request with the router + * Register a route handler for `DELETE` request. + * @param route {@link RouteConfig} - a route configuration. + * @param handler {@link RequestHandler} - a function to call to respond to an incoming request */ public delete

( route: RouteConfig, @@ -114,13 +136,14 @@ export class Router { /** * Returns all routes registered with the this router. * @returns List of registered routes. + * @internal */ public getRoutes() { return [...this.routes]; } /** - * Create the schemas for a route + * Create the validation schemas for a route * * @returns Route schemas if `validate` is specified on the route, otherwise * undefined. @@ -167,7 +190,7 @@ export class Router { } try { - const kibanaResponse = await handler(kibanaRequest, responseFactory); + const kibanaResponse = await handler(kibanaRequest, kibanaResponseFactory); return hapiResponseAdapter.handle(kibanaResponse); } catch (e) { log.error(e); @@ -176,7 +199,40 @@ export class Router { } } +/** + * A function executed when route path matched requested resource path. + * Request handler is expected to return a result of one of {@link KibanaResponseFactory} functions. + * @param request {@link KibanaRequest} - object containing information about requested resource, + * such as path, method, headers, parameters, query, body, etc. + * @param response {@link KibanaResponseFactory} - a set of helper functions used to respond to a request. + * + * @example + * ```ts + * const router = new Router('my-app'); + * // creates a route handler for GET request on 'my-app/path/{id}' path + * router.get( + * { + * path: 'path/{id}', + * // defines a validation schema for a named segment of the route path + * validate: { + * params: schema.object({ + * id: schema.string(), + * }), + * }, + * }, + * // function to execute to create a responses + * async (request, response) => { + * const data = await findObject(request.params.id); + * // creates a command to respond with 'not found' error + * if (!data) return response.notFound(); + * // creates a command to send found data to the client + * return response.ok(data); + * } + * ); + * ``` + * @public + */ export type RequestHandler

= ( request: KibanaRequest, TypeOf, TypeOf>, - createResponse: ResponseFactory + response: KibanaResponseFactory ) => KibanaResponse | Promise>; diff --git a/src/core/server/index.ts b/src/core/server/index.ts index 2bbcaa58f6af5..72a183f2dd1db 100644 --- a/src/core/server/index.ts +++ b/src/core/server/index.ts @@ -56,27 +56,41 @@ export { ElasticsearchErrorHelpers, APICaller, FakeRequest, - LegacyRequest, } from './elasticsearch'; export { AuthenticationHandler, AuthHeaders, AuthResultParams, + AuthStatus, AuthToolkit, + CustomHttpResponseOptions, GetAuthHeaders, + GetAuthState, + HttpResponseOptions, + HttpResponsePayload, + HttpServerSetup, + IsAuthenticated, KibanaRequest, KibanaRequestRoute, + KnownHeaders, + LegacyRequest, OnPreAuthHandler, OnPreAuthToolkit, OnPostAuthHandler, OnPostAuthToolkit, + RedirectResponseOptions, + RequestHandler, ResponseError, ResponseErrorMeta, + kibanaResponseFactory, + KibanaResponseFactory, + RouteConfig, Router, RouteMethod, RouteConfigOptions, - SessionStorageFactory, SessionStorage, + SessionStorageCookieOptions, + SessionStorageFactory, } from './http'; export { Logger, LoggerFactory, LogMeta, LogRecord, LogLevel } from './logging'; @@ -99,6 +113,7 @@ export { SavedObjectsClient, SavedObjectsClientContract, SavedObjectsCreateOptions, + SavedObjectsClientProviderOptions, SavedObjectsClientWrapperFactory, SavedObjectsClientWrapperOptions, SavedObjectsErrorHelpers, @@ -111,6 +126,16 @@ export { SavedObjectsService, SavedObjectsUpdateOptions, SavedObjectsUpdateResponse, + SavedObjectsExportOptions, + SavedObjectsImportError, + SavedObjectsImportConflictError, + SavedObjectsImportMissingReferencesError, + SavedObjectsImportUnknownError, + SavedObjectsImportUnsupportedTypeError, + SavedObjectsImportOptions, + SavedObjectsImportResponse, + SavedObjectsImportRetry, + SavedObjectsResolveImportErrorsOptions, } from './saved_objects'; export { RecursiveReadonly } from '../utils'; @@ -135,7 +160,6 @@ export interface CoreSetup { registerAuth: HttpServiceSetup['registerAuth']; registerOnPostAuth: HttpServiceSetup['registerOnPostAuth']; basePath: HttpServiceSetup['basePath']; - createNewServer: HttpServiceSetup['createNewServer']; isTlsEnabled: HttpServiceSetup['isTlsEnabled']; }; } diff --git a/src/core/server/plugins/plugin_context.ts b/src/core/server/plugins/plugin_context.ts index fcc8a26f51b4b..8ebd3e6b3c8b2 100644 --- a/src/core/server/plugins/plugin_context.ts +++ b/src/core/server/plugins/plugin_context.ts @@ -123,7 +123,6 @@ export function createPluginSetupContext( registerAuth: deps.http.registerAuth, registerOnPostAuth: deps.http.registerOnPostAuth, basePath: deps.http.basePath, - createNewServer: deps.http.createNewServer, isTlsEnabled: deps.http.isTlsEnabled, }, }; diff --git a/src/core/server/saved_objects/export/get_sorted_objects_for_export.test.ts b/src/core/server/saved_objects/export/get_sorted_objects_for_export.test.ts index 385de5b14565d..618a83b0fb68b 100644 --- a/src/core/server/saved_objects/export/get_sorted_objects_for_export.test.ts +++ b/src/core/server/saved_objects/export/get_sorted_objects_for_export.test.ts @@ -74,50 +74,134 @@ describe('getSortedObjectsForExport()', () => { const response = await readStreamToCompletion(exportStream); expect(response).toMatchInlineSnapshot(` -Array [ - Object { - "attributes": Object {}, - "id": "1", - "references": Array [], - "type": "index-pattern", - }, - Object { - "attributes": Object {}, - "id": "2", - "references": Array [ - Object { - "id": "1", - "name": "name", - "type": "index-pattern", - }, - ], - "type": "search", - }, -] -`); + Array [ + Object { + "attributes": Object {}, + "id": "1", + "references": Array [], + "type": "index-pattern", + }, + Object { + "attributes": Object {}, + "id": "2", + "references": Array [ + Object { + "id": "1", + "name": "name", + "type": "index-pattern", + }, + ], + "type": "search", + }, + ] + `); expect(savedObjectsClient.find).toMatchInlineSnapshot(` -[MockFunction] { - "calls": Array [ - Array [ - Object { - "perPage": 500, - "sortField": "_id", - "sortOrder": "asc", - "type": Array [ - "index-pattern", - "search", + [MockFunction] { + "calls": Array [ + Array [ + Object { + "namespace": undefined, + "perPage": 500, + "sortField": "_id", + "sortOrder": "asc", + "type": Array [ + "index-pattern", + "search", + ], + }, + ], ], - }, - ], - ], - "results": Array [ - Object { - "type": "return", - "value": Promise {}, - }, - ], -} -`); + "results": Array [ + Object { + "type": "return", + "value": Promise {}, + }, + ], + } + `); + }); + + test('exports from the provided namespace when present', async () => { + savedObjectsClient.find.mockResolvedValueOnce({ + total: 2, + saved_objects: [ + { + id: '2', + type: 'search', + attributes: {}, + references: [ + { + name: 'name', + type: 'index-pattern', + id: '1', + }, + ], + }, + { + id: '1', + type: 'index-pattern', + attributes: {}, + references: [], + }, + ], + per_page: 1, + page: 0, + }); + const exportStream = await getSortedObjectsForExport({ + savedObjectsClient, + exportSizeLimit: 500, + types: ['index-pattern', 'search'], + namespace: 'foo', + }); + + const response = await readStreamToCompletion(exportStream); + + expect(response).toMatchInlineSnapshot(` + Array [ + Object { + "attributes": Object {}, + "id": "1", + "references": Array [], + "type": "index-pattern", + }, + Object { + "attributes": Object {}, + "id": "2", + "references": Array [ + Object { + "id": "1", + "name": "name", + "type": "index-pattern", + }, + ], + "type": "search", + }, + ] + `); + expect(savedObjectsClient.find).toMatchInlineSnapshot(` + [MockFunction] { + "calls": Array [ + Array [ + Object { + "namespace": "foo", + "perPage": 500, + "sortField": "_id", + "sortOrder": "asc", + "type": Array [ + "index-pattern", + "search", + ], + }, + ], + ], + "results": Array [ + Object { + "type": "return", + "value": Promise {}, + }, + ], + } + `); }); test('export selected types throws error when exceeding exportSizeLimit', async () => { @@ -195,51 +279,54 @@ Array [ }); const response = await readStreamToCompletion(exportStream); expect(response).toMatchInlineSnapshot(` -Array [ - Object { - "attributes": Object {}, - "id": "1", - "references": Array [], - "type": "index-pattern", - }, - Object { - "attributes": Object {}, - "id": "2", - "references": Array [ - Object { - "id": "1", - "name": "name", - "type": "index-pattern", - }, - ], - "type": "search", - }, -] -`); - expect(savedObjectsClient.bulkGet).toMatchInlineSnapshot(` -[MockFunction] { - "calls": Array [ - Array [ Array [ Object { + "attributes": Object {}, "id": "1", + "references": Array [], "type": "index-pattern", }, Object { + "attributes": Object {}, "id": "2", + "references": Array [ + Object { + "id": "1", + "name": "name", + "type": "index-pattern", + }, + ], "type": "search", }, - ], - ], - ], - "results": Array [ - Object { - "type": "return", - "value": Promise {}, - }, - ], -} -`); + ] + `); + expect(savedObjectsClient.bulkGet).toMatchInlineSnapshot(` + [MockFunction] { + "calls": Array [ + Array [ + Array [ + Object { + "id": "1", + "type": "index-pattern", + }, + Object { + "id": "2", + "type": "search", + }, + ], + Object { + "namespace": undefined, + }, + ], + ], + "results": Array [ + Object { + "type": "return", + "value": Promise {}, + }, + ], + } + `); }); test('includes nested dependencies when passed in', async () => { @@ -283,59 +370,65 @@ Array [ }); const response = await readStreamToCompletion(exportStream); expect(response).toMatchInlineSnapshot(` -Array [ - Object { - "attributes": Object {}, - "id": "1", - "references": Array [], - "type": "index-pattern", - }, - Object { - "attributes": Object {}, - "id": "2", - "references": Array [ - Object { - "id": "1", - "name": "name", - "type": "index-pattern", - }, - ], - "type": "search", - }, -] -`); - expect(savedObjectsClient.bulkGet).toMatchInlineSnapshot(` -[MockFunction] { - "calls": Array [ - Array [ - Array [ - Object { - "id": "2", - "type": "search", - }, - ], - ], - Array [ Array [ Object { + "attributes": Object {}, "id": "1", + "references": Array [], "type": "index-pattern", }, - ], - ], - ], - "results": Array [ - Object { - "type": "return", - "value": Promise {}, - }, - Object { - "type": "return", - "value": Promise {}, - }, - ], -} -`); + Object { + "attributes": Object {}, + "id": "2", + "references": Array [ + Object { + "id": "1", + "name": "name", + "type": "index-pattern", + }, + ], + "type": "search", + }, + ] + `); + expect(savedObjectsClient.bulkGet).toMatchInlineSnapshot(` + [MockFunction] { + "calls": Array [ + Array [ + Array [ + Object { + "id": "2", + "type": "search", + }, + ], + Object { + "namespace": undefined, + }, + ], + Array [ + Array [ + Object { + "id": "1", + "type": "index-pattern", + }, + ], + Object { + "namespace": undefined, + }, + ], + ], + "results": Array [ + Object { + "type": "return", + "value": Promise {}, + }, + Object { + "type": "return", + "value": Promise {}, + }, + ], + } + `); }); test('export selected objects throws error when exceeding exportSizeLimit', async () => { diff --git a/src/core/server/saved_objects/export/get_sorted_objects_for_export.ts b/src/core/server/saved_objects/export/get_sorted_objects_for_export.ts index e09780574a25c..d8513bcfe72d3 100644 --- a/src/core/server/saved_objects/export/get_sorted_objects_for_export.ts +++ b/src/core/server/saved_objects/export/get_sorted_objects_for_export.ts @@ -23,17 +23,20 @@ import { SavedObjectsClientContract } from '../'; import { injectNestedDependencies } from './inject_nested_depdendencies'; import { sortObjects } from './sort_objects'; -interface ObjectToExport { - id: string; - type: string; -} - -interface ExportObjectsOptions { +/** + * Options controlling the export operation. + * @public + */ +export interface SavedObjectsExportOptions { types?: string[]; - objects?: ObjectToExport[]; + objects?: Array<{ + id: string; + type: string; + }>; savedObjectsClient: SavedObjectsClientContract; exportSizeLimit: number; includeReferencesDeep?: boolean; + namespace?: string; } async function fetchObjectsToExport({ @@ -41,17 +44,19 @@ async function fetchObjectsToExport({ types, exportSizeLimit, savedObjectsClient, + namespace, }: { - objects?: ObjectToExport[]; + objects?: SavedObjectsExportOptions['objects']; types?: string[]; exportSizeLimit: number; savedObjectsClient: SavedObjectsClientContract; + namespace?: string; }) { if (objects) { if (objects.length > exportSizeLimit) { throw Boom.badRequest(`Can't export more than ${exportSizeLimit} objects`); } - const bulkGetResult = await savedObjectsClient.bulkGet(objects); + const bulkGetResult = await savedObjectsClient.bulkGet(objects, { namespace }); const erroredObjects = bulkGetResult.saved_objects.filter(obj => !!obj.error); if (erroredObjects.length) { const err = Boom.badRequest(); @@ -67,6 +72,7 @@ async function fetchObjectsToExport({ sortField: '_id', sortOrder: 'asc', perPage: exportSizeLimit, + namespace, }); if (findResponse.total > exportSizeLimit) { throw Boom.badRequest(`Can't export more than ${exportSizeLimit} objects`); @@ -80,17 +86,19 @@ export async function getSortedObjectsForExport({ savedObjectsClient, exportSizeLimit, includeReferencesDeep = false, -}: ExportObjectsOptions) { + namespace, +}: SavedObjectsExportOptions) { const objectsToExport = await fetchObjectsToExport({ types, objects, savedObjectsClient, exportSizeLimit, + namespace, }); const exportedObjects = sortObjects( includeReferencesDeep - ? await injectNestedDependencies(objectsToExport, savedObjectsClient) + ? await injectNestedDependencies(objectsToExport, savedObjectsClient, namespace) : objectsToExport ); diff --git a/src/core/server/saved_objects/export/index.ts b/src/core/server/saved_objects/export/index.ts index eb52fcbecfda8..d994df2af627c 100644 --- a/src/core/server/saved_objects/export/index.ts +++ b/src/core/server/saved_objects/export/index.ts @@ -17,4 +17,7 @@ * under the License. */ -export { getSortedObjectsForExport } from './get_sorted_objects_for_export'; +export { + getSortedObjectsForExport, + SavedObjectsExportOptions, +} from './get_sorted_objects_for_export'; diff --git a/src/core/server/saved_objects/export/inject_nested_depdendencies.test.ts b/src/core/server/saved_objects/export/inject_nested_depdendencies.test.ts index 9a2548952de3d..2fa06550727f0 100644 --- a/src/core/server/saved_objects/export/inject_nested_depdendencies.test.ts +++ b/src/core/server/saved_objects/export/inject_nested_depdendencies.test.ts @@ -70,13 +70,13 @@ describe('getObjectReferencesToFetch()', () => { }); const result = getObjectReferencesToFetch(map); expect(result).toMatchInlineSnapshot(` -Array [ - Object { - "id": "1", - "type": "index-pattern", - }, -] -`); + Array [ + Object { + "id": "1", + "type": "index-pattern", + }, + ] + `); }); test(`doesn't deal with circular dependencies`, () => { @@ -137,15 +137,15 @@ describe('injectNestedDependencies', () => { ]; const result = await injectNestedDependencies(savedObjects, savedObjectsClient); expect(result).toMatchInlineSnapshot(` -Array [ - Object { - "attributes": Object {}, - "id": "1", - "references": Array [], - "type": "index-pattern", - }, -] -`); + Array [ + Object { + "attributes": Object {}, + "id": "1", + "references": Array [], + "type": "index-pattern", + }, + ] + `); }); test(`doesn't fetch references that are already fetched`, async () => { @@ -171,27 +171,27 @@ Array [ ]; const result = await injectNestedDependencies(savedObjects, savedObjectsClient); expect(result).toMatchInlineSnapshot(` -Array [ - Object { - "attributes": Object {}, - "id": "1", - "references": Array [], - "type": "index-pattern", - }, - Object { - "attributes": Object {}, - "id": "2", - "references": Array [ - Object { - "id": "1", - "name": "ref_0", - "type": "index-pattern", - }, - ], - "type": "search", - }, -] -`); + Array [ + Object { + "attributes": Object {}, + "id": "1", + "references": Array [], + "type": "index-pattern", + }, + Object { + "attributes": Object {}, + "id": "2", + "references": Array [ + Object { + "id": "1", + "name": "ref_0", + "type": "index-pattern", + }, + ], + "type": "search", + }, + ] + `); }); test('fetches dependencies at least one level deep', async () => { @@ -221,47 +221,50 @@ Array [ }); const result = await injectNestedDependencies(savedObjects, savedObjectsClient); expect(result).toMatchInlineSnapshot(` -Array [ - Object { - "attributes": Object {}, - "id": "2", - "references": Array [ - Object { - "id": "1", - "name": "ref_0", - "type": "index-pattern", - }, - ], - "type": "search", - }, - Object { - "attributes": Object {}, - "id": "1", - "references": Array [], - "type": "index-pattern", - }, -] -`); - expect(savedObjectsClient.bulkGet).toMatchInlineSnapshot(` -[MockFunction] { - "calls": Array [ - Array [ Array [ Object { + "attributes": Object {}, + "id": "2", + "references": Array [ + Object { + "id": "1", + "name": "ref_0", + "type": "index-pattern", + }, + ], + "type": "search", + }, + Object { + "attributes": Object {}, "id": "1", + "references": Array [], "type": "index-pattern", }, - ], - ], - ], - "results": Array [ - Object { - "type": "return", - "value": Promise {}, - }, - ], -} -`); + ] + `); + expect(savedObjectsClient.bulkGet).toMatchInlineSnapshot(` + [MockFunction] { + "calls": Array [ + Array [ + Array [ + Object { + "id": "1", + "type": "index-pattern", + }, + ], + Object { + "namespace": undefined, + }, + ], + ], + "results": Array [ + Object { + "type": "return", + "value": Promise {}, + }, + ], + } + `); }); test('fetches dependencies multiple levels deep', async () => { @@ -336,108 +339,114 @@ Array [ }); const result = await injectNestedDependencies(savedObjects, savedObjectsClient); expect(result).toMatchInlineSnapshot(` -Array [ - Object { - "attributes": Object {}, - "id": "5", - "references": Array [ - Object { - "id": "4", - "name": "panel_0", - "type": "visualization", - }, - Object { - "id": "3", - "name": "panel_1", - "type": "visualization", - }, - ], - "type": "dashboard", - }, - Object { - "attributes": Object {}, - "id": "4", - "references": Array [ - Object { - "id": "2", - "name": "ref_0", - "type": "search", - }, - ], - "type": "visualization", - }, - Object { - "attributes": Object {}, - "id": "3", - "references": Array [ - Object { - "id": "1", - "name": "ref_0", - "type": "index-pattern", - }, - ], - "type": "visualization", - }, - Object { - "attributes": Object {}, - "id": "2", - "references": Array [ - Object { - "id": "1", - "name": "ref_0", - "type": "index-pattern", - }, - ], - "type": "search", - }, - Object { - "attributes": Object {}, - "id": "1", - "references": Array [], - "type": "index-pattern", - }, -] -`); - expect(savedObjectsClient.bulkGet).toMatchInlineSnapshot(` -[MockFunction] { - "calls": Array [ - Array [ Array [ Object { + "attributes": Object {}, + "id": "5", + "references": Array [ + Object { + "id": "4", + "name": "panel_0", + "type": "visualization", + }, + Object { + "id": "3", + "name": "panel_1", + "type": "visualization", + }, + ], + "type": "dashboard", + }, + Object { + "attributes": Object {}, "id": "4", + "references": Array [ + Object { + "id": "2", + "name": "ref_0", + "type": "search", + }, + ], "type": "visualization", }, Object { + "attributes": Object {}, "id": "3", + "references": Array [ + Object { + "id": "1", + "name": "ref_0", + "type": "index-pattern", + }, + ], "type": "visualization", }, - ], - ], - Array [ - Array [ Object { + "attributes": Object {}, "id": "2", + "references": Array [ + Object { + "id": "1", + "name": "ref_0", + "type": "index-pattern", + }, + ], "type": "search", }, Object { + "attributes": Object {}, "id": "1", + "references": Array [], "type": "index-pattern", }, - ], - ], - ], - "results": Array [ - Object { - "type": "return", - "value": Promise {}, - }, - Object { - "type": "return", - "value": Promise {}, - }, - ], -} -`); + ] + `); + expect(savedObjectsClient.bulkGet).toMatchInlineSnapshot(` + [MockFunction] { + "calls": Array [ + Array [ + Array [ + Object { + "id": "4", + "type": "visualization", + }, + Object { + "id": "3", + "type": "visualization", + }, + ], + Object { + "namespace": undefined, + }, + ], + Array [ + Array [ + Object { + "id": "2", + "type": "search", + }, + Object { + "id": "1", + "type": "index-pattern", + }, + ], + Object { + "namespace": undefined, + }, + ], + ], + "results": Array [ + Object { + "type": "return", + "value": Promise {}, + }, + Object { + "type": "return", + "value": Promise {}, + }, + ], + } + `); }); test('throws error when bulkGet returns an error', async () => { @@ -505,52 +514,55 @@ Array [ }); const result = await injectNestedDependencies(savedObjects, savedObjectsClient); expect(result).toMatchInlineSnapshot(` -Array [ - Object { - "attributes": Object {}, - "id": "2", - "references": Array [ - Object { - "id": "1", - "name": "ref_0", - "type": "index-pattern", - }, - ], - "type": "search", - }, - Object { - "attributes": Object {}, - "id": "1", - "references": Array [ - Object { - "id": "2", - "name": "ref_0", - "type": "search", - }, - ], - "type": "index-pattern", - }, -] -`); - expect(savedObjectsClient.bulkGet).toMatchInlineSnapshot(` -[MockFunction] { - "calls": Array [ - Array [ Array [ Object { + "attributes": Object {}, + "id": "2", + "references": Array [ + Object { + "id": "1", + "name": "ref_0", + "type": "index-pattern", + }, + ], + "type": "search", + }, + Object { + "attributes": Object {}, "id": "1", + "references": Array [ + Object { + "id": "2", + "name": "ref_0", + "type": "search", + }, + ], "type": "index-pattern", }, - ], - ], - ], - "results": Array [ - Object { - "type": "return", - "value": Promise {}, - }, - ], -} -`); + ] + `); + expect(savedObjectsClient.bulkGet).toMatchInlineSnapshot(` + [MockFunction] { + "calls": Array [ + Array [ + Array [ + Object { + "id": "1", + "type": "index-pattern", + }, + ], + Object { + "namespace": undefined, + }, + ], + ], + "results": Array [ + Object { + "type": "return", + "value": Promise {}, + }, + ], + } + `); }); }); diff --git a/src/core/server/saved_objects/export/inject_nested_depdendencies.ts b/src/core/server/saved_objects/export/inject_nested_depdendencies.ts index ee9ce781ef9a5..82cb3e3cfe115 100644 --- a/src/core/server/saved_objects/export/inject_nested_depdendencies.ts +++ b/src/core/server/saved_objects/export/inject_nested_depdendencies.ts @@ -34,7 +34,8 @@ export function getObjectReferencesToFetch(savedObjectsMap: Map(); for (const savedObject of savedObjects) { @@ -42,7 +43,7 @@ export async function injectNestedDependencies( } let objectsToFetch = getObjectReferencesToFetch(savedObjectsMap); while (objectsToFetch.length > 0) { - const bulkGetResponse = await savedObjectsClient.bulkGet(objectsToFetch); + const bulkGetResponse = await savedObjectsClient.bulkGet(objectsToFetch, { namespace }); // Check for errors const erroredObjects = bulkGetResponse.saved_objects.filter(obj => !!obj.error); if (erroredObjects.length) { diff --git a/src/core/server/saved_objects/import/collect_saved_objects.ts b/src/core/server/saved_objects/import/collect_saved_objects.ts index 11add36e54fb6..fa2938109f6e7 100644 --- a/src/core/server/saved_objects/import/collect_saved_objects.ts +++ b/src/core/server/saved_objects/import/collect_saved_objects.ts @@ -26,7 +26,7 @@ import { } from '../../../../legacy/utils/streams'; import { SavedObject } from '../service'; import { createLimitStream } from './create_limit_stream'; -import { ImportError } from './types'; +import { SavedObjectsImportError } from './types'; interface CollectSavedObjectsOptions { readStream: Readable; @@ -41,7 +41,7 @@ export async function collectSavedObjects({ filter, supportedTypes, }: CollectSavedObjectsOptions) { - const errors: ImportError[] = []; + const errors: SavedObjectsImportError[] = []; const collectedObjects: SavedObject[] = await createPromiseFromStreams([ readStream, createLimitStream(objectLimit), diff --git a/src/core/server/saved_objects/import/create_objects_filter.ts b/src/core/server/saved_objects/import/create_objects_filter.ts index aacf8112f255a..684e44944002b 100644 --- a/src/core/server/saved_objects/import/create_objects_filter.ts +++ b/src/core/server/saved_objects/import/create_objects_filter.ts @@ -18,9 +18,9 @@ */ import { SavedObject } from '../service'; -import { Retry } from './types'; +import { SavedObjectsImportRetry } from './types'; -export function createObjectsFilter(retries: Retry[]) { +export function createObjectsFilter(retries: SavedObjectsImportRetry[]) { const retryKeys = new Set(retries.map(retry => `${retry.type}:${retry.id}`)); return (obj: SavedObject) => { return retryKeys.has(`${obj.type}:${obj.id}`); diff --git a/src/core/server/saved_objects/import/extract_errors.ts b/src/core/server/saved_objects/import/extract_errors.ts index 6ae9562a1f3aa..4c001cf8acf64 100644 --- a/src/core/server/saved_objects/import/extract_errors.ts +++ b/src/core/server/saved_objects/import/extract_errors.ts @@ -18,13 +18,13 @@ */ import { SavedObject } from '../service'; -import { ImportError } from './types'; +import { SavedObjectsImportError } from './types'; export function extractErrors( savedObjectResults: SavedObject[], savedObjectsToImport: SavedObject[] ) { - const errors: ImportError[] = []; + const errors: SavedObjectsImportError[] = []; const originalSavedObjectsMap = new Map(); for (const savedObject of savedObjectsToImport) { originalSavedObjectsMap.set(`${savedObject.type}:${savedObject.id}`, savedObject); diff --git a/src/core/server/saved_objects/import/import_saved_objects.test.ts b/src/core/server/saved_objects/import/import_saved_objects.test.ts index 80e5cc9a306f0..6ba4304574f49 100644 --- a/src/core/server/saved_objects/import/import_saved_objects.test.ts +++ b/src/core/server/saved_objects/import/import_saved_objects.test.ts @@ -86,11 +86,11 @@ describe('importSavedObjects()', () => { supportedTypes: [], }); expect(result).toMatchInlineSnapshot(` -Object { - "success": true, - "successCount": 0, -} -`); + Object { + "success": true, + "successCount": 0, + } + `); }); test('calls bulkCreate without overwrite', async () => { @@ -113,66 +113,151 @@ Object { supportedTypes: ['index-pattern', 'search', 'visualization', 'dashboard'], }); expect(result).toMatchInlineSnapshot(` -Object { - "success": true, - "successCount": 4, -} -`); + Object { + "success": true, + "successCount": 4, + } + `); expect(savedObjectsClient.bulkCreate).toMatchInlineSnapshot(` -[MockFunction] { - "calls": Array [ - Array [ - Array [ - Object { - "attributes": Object { - "title": "My Index Pattern", - }, - "id": "1", - "migrationVersion": Object {}, - "references": Array [], - "type": "index-pattern", - }, - Object { - "attributes": Object { - "title": "My Search", - }, - "id": "2", - "migrationVersion": Object {}, - "references": Array [], - "type": "search", - }, - Object { - "attributes": Object { - "title": "My Visualization", - }, - "id": "3", - "migrationVersion": Object {}, - "references": Array [], - "type": "visualization", - }, - Object { - "attributes": Object { - "title": "My Dashboard", + [MockFunction] { + "calls": Array [ + Array [ + Array [ + Object { + "attributes": Object { + "title": "My Index Pattern", + }, + "id": "1", + "migrationVersion": Object {}, + "references": Array [], + "type": "index-pattern", + }, + Object { + "attributes": Object { + "title": "My Search", + }, + "id": "2", + "migrationVersion": Object {}, + "references": Array [], + "type": "search", + }, + Object { + "attributes": Object { + "title": "My Visualization", + }, + "id": "3", + "migrationVersion": Object {}, + "references": Array [], + "type": "visualization", + }, + Object { + "attributes": Object { + "title": "My Dashboard", + }, + "id": "4", + "migrationVersion": Object {}, + "references": Array [], + "type": "dashboard", + }, + ], + Object { + "namespace": undefined, + "overwrite": false, + }, + ], + ], + "results": Array [ + Object { + "type": "return", + "value": Promise {}, }, - "id": "4", - "migrationVersion": Object {}, - "references": Array [], - "type": "dashboard", - }, - ], - Object { - "overwrite": false, + ], + } + `); + }); + + test('uses the provided namespace when present', async () => { + const readStream = new Readable({ + objectMode: true, + read() { + savedObjects.forEach(obj => this.push(obj)); + this.push(null); }, - ], - ], - "results": Array [ - Object { - "type": "return", - "value": Promise {}, - }, - ], -} -`); + }); + savedObjectsClient.find.mockResolvedValueOnce({ saved_objects: [] }); + savedObjectsClient.bulkCreate.mockResolvedValue({ + saved_objects: savedObjects, + }); + const result = await importSavedObjects({ + readStream, + objectLimit: 4, + overwrite: false, + savedObjectsClient, + supportedTypes: ['index-pattern', 'search', 'visualization', 'dashboard'], + namespace: 'foo', + }); + expect(result).toMatchInlineSnapshot(` + Object { + "success": true, + "successCount": 4, + } + `); + expect(savedObjectsClient.bulkCreate).toMatchInlineSnapshot(` + [MockFunction] { + "calls": Array [ + Array [ + Array [ + Object { + "attributes": Object { + "title": "My Index Pattern", + }, + "id": "1", + "migrationVersion": Object {}, + "references": Array [], + "type": "index-pattern", + }, + Object { + "attributes": Object { + "title": "My Search", + }, + "id": "2", + "migrationVersion": Object {}, + "references": Array [], + "type": "search", + }, + Object { + "attributes": Object { + "title": "My Visualization", + }, + "id": "3", + "migrationVersion": Object {}, + "references": Array [], + "type": "visualization", + }, + Object { + "attributes": Object { + "title": "My Dashboard", + }, + "id": "4", + "migrationVersion": Object {}, + "references": Array [], + "type": "dashboard", + }, + ], + Object { + "namespace": "foo", + "overwrite": false, + }, + ], + ], + "results": Array [ + Object { + "type": "return", + "value": Promise {}, + }, + ], + } + `); }); test('calls bulkCreate with overwrite', async () => { @@ -195,66 +280,67 @@ Object { supportedTypes: ['index-pattern', 'search', 'visualization', 'dashboard'], }); expect(result).toMatchInlineSnapshot(` -Object { - "success": true, - "successCount": 4, -} -`); + Object { + "success": true, + "successCount": 4, + } + `); expect(savedObjectsClient.bulkCreate).toMatchInlineSnapshot(` -[MockFunction] { - "calls": Array [ - Array [ - Array [ - Object { - "attributes": Object { - "title": "My Index Pattern", - }, - "id": "1", - "migrationVersion": Object {}, - "references": Array [], - "type": "index-pattern", - }, - Object { - "attributes": Object { - "title": "My Search", - }, - "id": "2", - "migrationVersion": Object {}, - "references": Array [], - "type": "search", - }, - Object { - "attributes": Object { - "title": "My Visualization", - }, - "id": "3", - "migrationVersion": Object {}, - "references": Array [], - "type": "visualization", - }, - Object { - "attributes": Object { - "title": "My Dashboard", + [MockFunction] { + "calls": Array [ + Array [ + Array [ + Object { + "attributes": Object { + "title": "My Index Pattern", + }, + "id": "1", + "migrationVersion": Object {}, + "references": Array [], + "type": "index-pattern", + }, + Object { + "attributes": Object { + "title": "My Search", + }, + "id": "2", + "migrationVersion": Object {}, + "references": Array [], + "type": "search", + }, + Object { + "attributes": Object { + "title": "My Visualization", + }, + "id": "3", + "migrationVersion": Object {}, + "references": Array [], + "type": "visualization", + }, + Object { + "attributes": Object { + "title": "My Dashboard", + }, + "id": "4", + "migrationVersion": Object {}, + "references": Array [], + "type": "dashboard", + }, + ], + Object { + "namespace": undefined, + "overwrite": true, + }, + ], + ], + "results": Array [ + Object { + "type": "return", + "value": Promise {}, }, - "id": "4", - "migrationVersion": Object {}, - "references": Array [], - "type": "dashboard", - }, - ], - Object { - "overwrite": true, - }, - ], - ], - "results": Array [ - Object { - "type": "return", - "value": Promise {}, - }, - ], -} -`); + ], + } + `); }); test('extracts errors for conflicts', async () => { @@ -284,45 +370,45 @@ Object { supportedTypes: ['index-pattern', 'search', 'visualization', 'dashboard'], }); expect(result).toMatchInlineSnapshot(` -Object { - "errors": Array [ - Object { - "error": Object { - "type": "conflict", - }, - "id": "1", - "title": "My Index Pattern", - "type": "index-pattern", - }, - Object { - "error": Object { - "type": "conflict", - }, - "id": "2", - "title": "My Search", - "type": "search", - }, - Object { - "error": Object { - "type": "conflict", - }, - "id": "3", - "title": "My Visualization", - "type": "visualization", - }, - Object { - "error": Object { - "type": "conflict", - }, - "id": "4", - "title": "My Dashboard", - "type": "dashboard", - }, - ], - "success": false, - "successCount": 0, -} -`); + Object { + "errors": Array [ + Object { + "error": Object { + "type": "conflict", + }, + "id": "1", + "title": "My Index Pattern", + "type": "index-pattern", + }, + Object { + "error": Object { + "type": "conflict", + }, + "id": "2", + "title": "My Search", + "type": "search", + }, + Object { + "error": Object { + "type": "conflict", + }, + "id": "3", + "title": "My Visualization", + "type": "visualization", + }, + Object { + "error": Object { + "type": "conflict", + }, + "id": "4", + "title": "My Dashboard", + "type": "dashboard", + }, + ], + "success": false, + "successCount": 0, + } + `); }); test('validates references', async () => { @@ -380,56 +466,59 @@ Object { supportedTypes: ['index-pattern', 'search', 'visualization', 'dashboard'], }); expect(result).toMatchInlineSnapshot(` -Object { - "errors": Array [ - Object { - "error": Object { - "blocking": Array [ + Object { + "errors": Array [ Object { - "id": "3", - "type": "visualization", + "error": Object { + "blocking": Array [ + Object { + "id": "3", + "type": "visualization", + }, + ], + "references": Array [ + Object { + "id": "2", + "type": "index-pattern", + }, + ], + "type": "missing_references", + }, + "id": "1", + "title": "My Search", + "type": "search", }, ], - "references": Array [ + "success": false, + "successCount": 0, + } + `); + expect(savedObjectsClient.bulkGet).toMatchInlineSnapshot(` + [MockFunction] { + "calls": Array [ + Array [ + Array [ + Object { + "fields": Array [ + "id", + ], + "id": "2", + "type": "index-pattern", + }, + ], + Object { + "namespace": undefined, + }, + ], + ], + "results": Array [ Object { - "id": "2", - "type": "index-pattern", + "type": "return", + "value": Promise {}, }, ], - "type": "missing_references", - }, - "id": "1", - "title": "My Search", - "type": "search", - }, - ], - "success": false, - "successCount": 0, -} -`); - expect(savedObjectsClient.bulkGet).toMatchInlineSnapshot(` -[MockFunction] { - "calls": Array [ - Array [ - Array [ - Object { - "fields": Array [ - "id", - ], - "id": "2", - "type": "index-pattern", - }, - ], - ], - ], - "results": Array [ - Object { - "type": "return", - "value": Promise {}, - }, - ], -} -`); + } + `); }); test('validates supported types', async () => { @@ -453,75 +542,76 @@ Object { supportedTypes: ['index-pattern', 'search', 'visualization', 'dashboard'], }); expect(result).toMatchInlineSnapshot(` -Object { - "errors": Array [ - Object { - "error": Object { - "type": "unsupported_type", - }, - "id": "1", - "title": "my title", - "type": "wigwags", - }, - ], - "success": false, - "successCount": 4, -} -`); - expect(savedObjectsClient.bulkCreate).toMatchInlineSnapshot(` -[MockFunction] { - "calls": Array [ - Array [ - Array [ - Object { - "attributes": Object { - "title": "My Index Pattern", - }, - "id": "1", - "migrationVersion": Object {}, - "references": Array [], - "type": "index-pattern", - }, - Object { - "attributes": Object { - "title": "My Search", - }, - "id": "2", - "migrationVersion": Object {}, - "references": Array [], - "type": "search", - }, - Object { - "attributes": Object { - "title": "My Visualization", + Object { + "errors": Array [ + Object { + "error": Object { + "type": "unsupported_type", + }, + "id": "1", + "title": "my title", + "type": "wigwags", }, - "id": "3", - "migrationVersion": Object {}, - "references": Array [], - "type": "visualization", - }, - Object { - "attributes": Object { - "title": "My Dashboard", + ], + "success": false, + "successCount": 4, + } + `); + expect(savedObjectsClient.bulkCreate).toMatchInlineSnapshot(` + [MockFunction] { + "calls": Array [ + Array [ + Array [ + Object { + "attributes": Object { + "title": "My Index Pattern", + }, + "id": "1", + "migrationVersion": Object {}, + "references": Array [], + "type": "index-pattern", + }, + Object { + "attributes": Object { + "title": "My Search", + }, + "id": "2", + "migrationVersion": Object {}, + "references": Array [], + "type": "search", + }, + Object { + "attributes": Object { + "title": "My Visualization", + }, + "id": "3", + "migrationVersion": Object {}, + "references": Array [], + "type": "visualization", + }, + Object { + "attributes": Object { + "title": "My Dashboard", + }, + "id": "4", + "migrationVersion": Object {}, + "references": Array [], + "type": "dashboard", + }, + ], + Object { + "namespace": undefined, + "overwrite": false, + }, + ], + ], + "results": Array [ + Object { + "type": "return", + "value": Promise {}, }, - "id": "4", - "migrationVersion": Object {}, - "references": Array [], - "type": "dashboard", - }, - ], - Object { - "overwrite": false, - }, - ], - ], - "results": Array [ - Object { - "type": "return", - "value": Promise {}, - }, - ], -} -`); + ], + } + `); }); }); diff --git a/src/core/server/saved_objects/import/import_saved_objects.ts b/src/core/server/saved_objects/import/import_saved_objects.ts index 10c1350c4c579..ef3b4a214c2c2 100644 --- a/src/core/server/saved_objects/import/import_saved_objects.ts +++ b/src/core/server/saved_objects/import/import_saved_objects.ts @@ -17,26 +17,14 @@ * under the License. */ -import { Readable } from 'stream'; import { collectSavedObjects } from './collect_saved_objects'; import { extractErrors } from './extract_errors'; -import { ImportError } from './types'; +import { + SavedObjectsImportError, + SavedObjectsImportResponse, + SavedObjectsImportOptions, +} from './types'; import { validateReferences } from './validate_references'; -import { SavedObjectsClientContract } from '../'; - -interface ImportSavedObjectsOptions { - readStream: Readable; - objectLimit: number; - overwrite: boolean; - savedObjectsClient: SavedObjectsClientContract; - supportedTypes: string[]; -} - -interface ImportResponse { - success: boolean; - successCount: number; - errors?: ImportError[]; -} export async function importSavedObjects({ readStream, @@ -44,8 +32,9 @@ export async function importSavedObjects({ overwrite, savedObjectsClient, supportedTypes, -}: ImportSavedObjectsOptions): Promise { - let errorAccumulator: ImportError[] = []; + namespace, +}: SavedObjectsImportOptions): Promise { + let errorAccumulator: SavedObjectsImportError[] = []; // Get the objects to import const { @@ -57,7 +46,8 @@ export async function importSavedObjects({ // Validate references const { filteredObjects, errors: validationErrors } = await validateReferences( objectsFromStream, - savedObjectsClient + savedObjectsClient, + namespace ); errorAccumulator = [...errorAccumulator, ...validationErrors]; @@ -73,6 +63,7 @@ export async function importSavedObjects({ // Create objects in bulk const bulkCreateResult = await savedObjectsClient.bulkCreate(filteredObjects, { overwrite, + namespace, }); errorAccumulator = [ ...errorAccumulator, diff --git a/src/core/server/saved_objects/import/index.ts b/src/core/server/saved_objects/import/index.ts index aad06931c330e..95fa8aa192f3e 100644 --- a/src/core/server/saved_objects/import/index.ts +++ b/src/core/server/saved_objects/import/index.ts @@ -19,3 +19,14 @@ export { importSavedObjects } from './import_saved_objects'; export { resolveImportErrors } from './resolve_import_errors'; +export { + SavedObjectsImportResponse, + SavedObjectsImportError, + SavedObjectsImportOptions, + SavedObjectsImportConflictError, + SavedObjectsImportMissingReferencesError, + SavedObjectsImportUnknownError, + SavedObjectsImportUnsupportedTypeError, + SavedObjectsResolveImportErrorsOptions, + SavedObjectsImportRetry, +} from './types'; diff --git a/src/core/server/saved_objects/import/resolve_import_errors.test.ts b/src/core/server/saved_objects/import/resolve_import_errors.test.ts index d3f36852fd796..783b0d6a61be6 100644 --- a/src/core/server/saved_objects/import/resolve_import_errors.test.ts +++ b/src/core/server/saved_objects/import/resolve_import_errors.test.ts @@ -96,11 +96,11 @@ describe('resolveImportErrors()', () => { supportedTypes: ['index-pattern', 'search', 'visualization', 'dashboard'], }); expect(result).toMatchInlineSnapshot(` -Object { - "success": true, - "successCount": 0, -} -`); + Object { + "success": true, + "successCount": 0, + } + `); expect(savedObjectsClient.bulkCreate).toMatchInlineSnapshot(`[MockFunction]`); }); @@ -130,36 +130,39 @@ Object { supportedTypes: ['index-pattern', 'search', 'visualization', 'dashboard'], }); expect(result).toMatchInlineSnapshot(` -Object { - "success": true, - "successCount": 1, -} -`); + Object { + "success": true, + "successCount": 1, + } + `); expect(savedObjectsClient.bulkCreate).toMatchInlineSnapshot(` -[MockFunction] { - "calls": Array [ - Array [ - Array [ - Object { - "attributes": Object { - "title": "My Visualization", + [MockFunction] { + "calls": Array [ + Array [ + Array [ + Object { + "attributes": Object { + "title": "My Visualization", + }, + "id": "3", + "migrationVersion": Object {}, + "references": Array [], + "type": "visualization", + }, + ], + Object { + "namespace": undefined, + }, + ], + ], + "results": Array [ + Object { + "type": "return", + "value": Promise {}, }, - "id": "3", - "migrationVersion": Object {}, - "references": Array [], - "type": "visualization", - }, - ], - ], - ], - "results": Array [ - Object { - "type": "return", - "value": Promise {}, - }, - ], -} -`); + ], + } + `); }); test('works with overwrites', async () => { @@ -188,39 +191,40 @@ Object { supportedTypes: ['index-pattern', 'search', 'visualization', 'dashboard'], }); expect(result).toMatchInlineSnapshot(` -Object { - "success": true, - "successCount": 1, -} -`); + Object { + "success": true, + "successCount": 1, + } + `); expect(savedObjectsClient.bulkCreate).toMatchInlineSnapshot(` -[MockFunction] { - "calls": Array [ - Array [ - Array [ - Object { - "attributes": Object { - "title": "My Index Pattern", + [MockFunction] { + "calls": Array [ + Array [ + Array [ + Object { + "attributes": Object { + "title": "My Index Pattern", + }, + "id": "1", + "migrationVersion": Object {}, + "references": Array [], + "type": "index-pattern", + }, + ], + Object { + "namespace": undefined, + "overwrite": true, + }, + ], + ], + "results": Array [ + Object { + "type": "return", + "value": Promise {}, }, - "id": "1", - "migrationVersion": Object {}, - "references": Array [], - "type": "index-pattern", - }, - ], - Object { - "overwrite": true, - }, - ], - ], - "results": Array [ - Object { - "type": "return", - "value": Promise {}, - }, - ], -} -`); + ], + } + `); }); test('works wtih replaceReferences', async () => { @@ -255,42 +259,45 @@ Object { supportedTypes: ['index-pattern', 'search', 'visualization', 'dashboard'], }); expect(result).toMatchInlineSnapshot(` -Object { - "success": true, - "successCount": 1, -} -`); + Object { + "success": true, + "successCount": 1, + } + `); expect(savedObjectsClient.bulkCreate).toMatchInlineSnapshot(` -[MockFunction] { - "calls": Array [ - Array [ - Array [ - Object { - "attributes": Object { - "title": "My Dashboard", - }, - "id": "4", - "migrationVersion": Object {}, - "references": Array [ + [MockFunction] { + "calls": Array [ + Array [ + Array [ + Object { + "attributes": Object { + "title": "My Dashboard", + }, + "id": "4", + "migrationVersion": Object {}, + "references": Array [ + Object { + "id": "13", + "name": "panel_0", + "type": "visualization", + }, + ], + "type": "dashboard", + }, + ], Object { - "id": "13", - "name": "panel_0", - "type": "visualization", + "namespace": undefined, }, ], - "type": "dashboard", - }, - ], - ], - ], - "results": Array [ - Object { - "type": "return", - "value": Promise {}, - }, - ], -} -`); + ], + "results": Array [ + Object { + "type": "return", + "value": Promise {}, + }, + ], + } + `); }); test('extracts errors for conflicts', async () => { @@ -324,45 +331,45 @@ Object { supportedTypes: ['index-pattern', 'search', 'visualization', 'dashboard'], }); expect(result).toMatchInlineSnapshot(` -Object { - "errors": Array [ - Object { - "error": Object { - "type": "conflict", - }, - "id": "1", - "title": "My Index Pattern", - "type": "index-pattern", - }, - Object { - "error": Object { - "type": "conflict", - }, - "id": "2", - "title": "My Search", - "type": "search", - }, - Object { - "error": Object { - "type": "conflict", - }, - "id": "3", - "title": "My Visualization", - "type": "visualization", - }, - Object { - "error": Object { - "type": "conflict", - }, - "id": "4", - "title": "My Dashboard", - "type": "dashboard", - }, - ], - "success": false, - "successCount": 0, -} -`); + Object { + "errors": Array [ + Object { + "error": Object { + "type": "conflict", + }, + "id": "1", + "title": "My Index Pattern", + "type": "index-pattern", + }, + Object { + "error": Object { + "type": "conflict", + }, + "id": "2", + "title": "My Search", + "type": "search", + }, + Object { + "error": Object { + "type": "conflict", + }, + "id": "3", + "title": "My Visualization", + "type": "visualization", + }, + Object { + "error": Object { + "type": "conflict", + }, + "id": "4", + "title": "My Dashboard", + "type": "dashboard", + }, + ], + "success": false, + "successCount": 0, + } + `); }); test('validates references', async () => { @@ -433,56 +440,59 @@ Object { supportedTypes: ['index-pattern', 'search', 'visualization', 'dashboard'], }); expect(result).toMatchInlineSnapshot(` -Object { - "errors": Array [ - Object { - "error": Object { - "blocking": Array [ + Object { + "errors": Array [ Object { - "id": "3", - "type": "visualization", + "error": Object { + "blocking": Array [ + Object { + "id": "3", + "type": "visualization", + }, + ], + "references": Array [ + Object { + "id": "2", + "type": "index-pattern", + }, + ], + "type": "missing_references", + }, + "id": "1", + "title": "My Search", + "type": "search", }, ], - "references": Array [ + "success": false, + "successCount": 0, + } + `); + expect(savedObjectsClient.bulkGet).toMatchInlineSnapshot(` + [MockFunction] { + "calls": Array [ + Array [ + Array [ + Object { + "fields": Array [ + "id", + ], + "id": "2", + "type": "index-pattern", + }, + ], + Object { + "namespace": undefined, + }, + ], + ], + "results": Array [ Object { - "id": "2", - "type": "index-pattern", + "type": "return", + "value": Promise {}, }, ], - "type": "missing_references", - }, - "id": "1", - "title": "My Search", - "type": "search", - }, - ], - "success": false, - "successCount": 0, -} -`); - expect(savedObjectsClient.bulkGet).toMatchInlineSnapshot(` -[MockFunction] { - "calls": Array [ - Array [ - Array [ - Object { - "fields": Array [ - "id", - ], - "id": "2", - "type": "index-pattern", - }, - ], - ], - ], - "results": Array [ - Object { - "type": "return", - "value": Promise {}, - }, - ], -} -`); + } + `); }); test('validates object types', async () => { @@ -512,21 +522,67 @@ Object { supportedTypes: ['index-pattern', 'search', 'visualization', 'dashboard'], }); expect(result).toMatchInlineSnapshot(` -Object { - "errors": Array [ - Object { - "error": Object { - "type": "unsupported_type", - }, - "id": "1", - "title": "my title", - "type": "wigwags", - }, - ], - "success": false, - "successCount": 0, -} -`); + Object { + "errors": Array [ + Object { + "error": Object { + "type": "unsupported_type", + }, + "id": "1", + "title": "my title", + "type": "wigwags", + }, + ], + "success": false, + "successCount": 0, + } + `); expect(savedObjectsClient.bulkCreate).toMatchInlineSnapshot(`[MockFunction]`); }); + + test('uses namespace when provided', async () => { + const readStream = new Readable({ + objectMode: true, + read() { + savedObjects.forEach(obj => this.push(obj)); + this.push(null); + }, + }); + savedObjectsClient.bulkCreate.mockResolvedValue({ + saved_objects: savedObjects.filter(obj => obj.type === 'index-pattern' && obj.id === '1'), + }); + const result = await resolveImportErrors({ + readStream, + objectLimit: 4, + retries: [ + { + type: 'index-pattern', + id: '1', + overwrite: true, + replaceReferences: [], + }, + ], + savedObjectsClient, + supportedTypes: ['index-pattern', 'search', 'visualization', 'dashboard'], + namespace: 'foo', + }); + expect(result).toMatchInlineSnapshot(` + Object { + "success": true, + "successCount": 1, + } + `); + expect(savedObjectsClient.bulkCreate).toHaveBeenCalledWith( + [ + { + attributes: { title: 'My Index Pattern' }, + id: '1', + migrationVersion: {}, + references: [], + type: 'index-pattern', + }, + ], + { namespace: 'foo', overwrite: true } + ); + }); }); diff --git a/src/core/server/saved_objects/import/resolve_import_errors.ts b/src/core/server/saved_objects/import/resolve_import_errors.ts index 5cd4d2fca740c..c04827dc98ab5 100644 --- a/src/core/server/saved_objects/import/resolve_import_errors.ts +++ b/src/core/server/saved_objects/import/resolve_import_errors.ts @@ -17,38 +17,27 @@ * under the License. */ -import { Readable } from 'stream'; -import { SavedObjectsClientContract } from '../'; import { collectSavedObjects } from './collect_saved_objects'; import { createObjectsFilter } from './create_objects_filter'; import { extractErrors } from './extract_errors'; import { splitOverwrites } from './split_overwrites'; -import { ImportError, Retry } from './types'; +import { + SavedObjectsImportError, + SavedObjectsImportResponse, + SavedObjectsResolveImportErrorsOptions, +} from './types'; import { validateReferences } from './validate_references'; -interface ResolveImportErrorsOptions { - readStream: Readable; - objectLimit: number; - savedObjectsClient: SavedObjectsClientContract; - retries: Retry[]; - supportedTypes: string[]; -} - -interface ImportResponse { - success: boolean; - successCount: number; - errors?: ImportError[]; -} - export async function resolveImportErrors({ readStream, objectLimit, retries, savedObjectsClient, supportedTypes, -}: ResolveImportErrorsOptions): Promise { + namespace, +}: SavedObjectsResolveImportErrorsOptions): Promise { let successCount = 0; - let errorAccumulator: ImportError[] = []; + let errorAccumulator: SavedObjectsImportError[] = []; const filter = createObjectsFilter(retries); // Get the objects to resolve errors @@ -89,7 +78,8 @@ export async function resolveImportErrors({ // Validate references const { filteredObjects, errors: validationErrors } = await validateReferences( objectsToResolve, - savedObjectsClient + savedObjectsClient, + namespace ); errorAccumulator = [...errorAccumulator, ...validationErrors]; @@ -98,6 +88,7 @@ export async function resolveImportErrors({ if (objectsToOverwrite.length) { const bulkCreateResult = await savedObjectsClient.bulkCreate(objectsToOverwrite, { overwrite: true, + namespace, }); errorAccumulator = [ ...errorAccumulator, @@ -106,7 +97,9 @@ export async function resolveImportErrors({ successCount += bulkCreateResult.saved_objects.filter(obj => !obj.error).length; } if (objectsToNotOverwrite.length) { - const bulkCreateResult = await savedObjectsClient.bulkCreate(objectsToNotOverwrite); + const bulkCreateResult = await savedObjectsClient.bulkCreate(objectsToNotOverwrite, { + namespace, + }); errorAccumulator = [ ...errorAccumulator, ...extractErrors(bulkCreateResult.saved_objects, objectsToNotOverwrite), diff --git a/src/core/server/saved_objects/import/split_overwrites.ts b/src/core/server/saved_objects/import/split_overwrites.ts index 5609308f755f3..f49b634c8d9f2 100644 --- a/src/core/server/saved_objects/import/split_overwrites.ts +++ b/src/core/server/saved_objects/import/split_overwrites.ts @@ -18,9 +18,9 @@ */ import { SavedObject } from '../service'; -import { Retry } from './types'; +import { SavedObjectsImportRetry } from './types'; -export function splitOverwrites(savedObjects: SavedObject[], retries: Retry[]) { +export function splitOverwrites(savedObjects: SavedObject[], retries: SavedObjectsImportRetry[]) { const objectsToOverwrite: SavedObject[] = []; const objectsToNotOverwrite: SavedObject[] = []; const overwrites = retries diff --git a/src/core/server/saved_objects/import/types.ts b/src/core/server/saved_objects/import/types.ts index ccefe178f38d5..cc16a1697d9a0 100644 --- a/src/core/server/saved_objects/import/types.ts +++ b/src/core/server/saved_objects/import/types.ts @@ -17,7 +17,14 @@ * under the License. */ -export interface Retry { +import { Readable } from 'stream'; +import { SavedObjectsClientContract } from '../service'; + +/** + * Describes a retry operation for importing a saved object. + * @public + */ +export interface SavedObjectsImportRetry { type: string; id: string; overwrite: boolean; @@ -28,21 +35,37 @@ export interface Retry { }>; } -export interface ConflictError { +/** + * Represents a failure to import due to a conflict. + * @public + */ +export interface SavedObjectsImportConflictError { type: 'conflict'; } -export interface UnsupportedTypeError { +/** + * Represents a failure to import due to having an unsupported saved object type. + * @public + */ +export interface SavedObjectsImportUnsupportedTypeError { type: 'unsupported_type'; } -export interface UnknownError { +/** + * Represents a failure to import due to an unknown reason. + * @public + */ +export interface SavedObjectsImportUnknownError { type: 'unknown'; message: string; statusCode: number; } -export interface MissingReferencesError { +/** + * Represents a failure to import due to missing references. + * @public + */ +export interface SavedObjectsImportMissingReferencesError { type: 'missing_references'; references: Array<{ type: string; @@ -54,9 +77,53 @@ export interface MissingReferencesError { }>; } -export interface ImportError { +/** + * Represents a failure to import. + * @public + */ +export interface SavedObjectsImportError { id: string; type: string; title?: string; - error: ConflictError | UnsupportedTypeError | MissingReferencesError | UnknownError; + error: + | SavedObjectsImportConflictError + | SavedObjectsImportUnsupportedTypeError + | SavedObjectsImportMissingReferencesError + | SavedObjectsImportUnknownError; +} + +/** + * The response describing the result of an import. + * @public + */ +export interface SavedObjectsImportResponse { + success: boolean; + successCount: number; + errors?: SavedObjectsImportError[]; +} + +/** + * Options to control the import operation. + * @public + */ +export interface SavedObjectsImportOptions { + readStream: Readable; + objectLimit: number; + overwrite: boolean; + savedObjectsClient: SavedObjectsClientContract; + supportedTypes: string[]; + namespace?: string; +} + +/** + * Options to control the "resolve import" operation. + * @public + */ +export interface SavedObjectsResolveImportErrorsOptions { + readStream: Readable; + objectLimit: number; + savedObjectsClient: SavedObjectsClientContract; + retries: SavedObjectsImportRetry[]; + supportedTypes: string[]; + namespace?: string; } diff --git a/src/core/server/saved_objects/import/validate_references.test.ts b/src/core/server/saved_objects/import/validate_references.test.ts index e69ca99fb10f2..1a558b3d82b32 100644 --- a/src/core/server/saved_objects/import/validate_references.test.ts +++ b/src/core/server/saved_objects/import/validate_references.test.ts @@ -107,6 +107,9 @@ describe('getNonExistingReferenceAsKeys()', () => { "type": "index-pattern", }, ], + Object { + "namespace": undefined, + }, ], ], "results": Array [ @@ -206,6 +209,9 @@ describe('getNonExistingReferenceAsKeys()', () => { "type": "search", }, ], + Object { + "namespace": undefined, + }, ], ], "results": Array [ @@ -434,6 +440,9 @@ Object { "type": "search", }, ], + Object { + "namespace": undefined, + }, ], ], "results": Array [ diff --git a/src/core/server/saved_objects/import/validate_references.ts b/src/core/server/saved_objects/import/validate_references.ts index 2e3c1ef5293b3..ad3f73b68f6e0 100644 --- a/src/core/server/saved_objects/import/validate_references.ts +++ b/src/core/server/saved_objects/import/validate_references.ts @@ -19,7 +19,7 @@ import Boom from 'boom'; import { SavedObject, SavedObjectsClientContract } from '../'; -import { ImportError } from './types'; +import { SavedObjectsImportError } from './types'; const REF_TYPES_TO_VLIDATE = ['index-pattern', 'search']; @@ -29,7 +29,8 @@ function filterReferencesToValidate({ type }: { type: string }) { export async function getNonExistingReferenceAsKeys( savedObjects: SavedObject[], - savedObjectsClient: SavedObjectsClientContract + savedObjectsClient: SavedObjectsClientContract, + namespace?: string ) { const collector = new Map(); // Collect all references within objects @@ -50,7 +51,7 @@ export async function getNonExistingReferenceAsKeys( // Fetch references to see if they exist const bulkGetOpts = Array.from(collector.values()).map(obj => ({ ...obj, fields: ['id'] })); - const bulkGetResponse = await savedObjectsClient.bulkGet(bulkGetOpts); + const bulkGetResponse = await savedObjectsClient.bulkGet(bulkGetOpts, { namespace }); // Error handling const erroredObjects = bulkGetResponse.saved_objects.filter( @@ -77,12 +78,14 @@ export async function getNonExistingReferenceAsKeys( export async function validateReferences( savedObjects: SavedObject[], - savedObjectsClient: SavedObjectsClientContract + savedObjectsClient: SavedObjectsClientContract, + namespace?: string ) { - const errorMap: { [key: string]: ImportError } = {}; + const errorMap: { [key: string]: SavedObjectsImportError } = {}; const nonExistingReferenceKeys = await getNonExistingReferenceAsKeys( savedObjects, - savedObjectsClient + savedObjectsClient, + namespace ); // Filter out objects with missing references, add to error object diff --git a/src/core/server/saved_objects/index.ts b/src/core/server/saved_objects/index.ts index 623c722eb95b1..ef0362e0eb915 100644 --- a/src/core/server/saved_objects/index.ts +++ b/src/core/server/saved_objects/index.ts @@ -23,4 +23,8 @@ export { SavedObjectsSchema } from './schema'; export { SavedObjectsManagement } from './management'; +export * from './import'; + +export { getSortedObjectsForExport, SavedObjectsExportOptions } from './export'; + export { SavedObjectsSerializer, RawDoc as SavedObjectsRawDoc } from './serialization'; diff --git a/src/core/server/saved_objects/service/index.ts b/src/core/server/saved_objects/service/index.ts index 697e1d2d41471..386539e755d9a 100644 --- a/src/core/server/saved_objects/service/index.ts +++ b/src/core/server/saved_objects/service/index.ts @@ -17,8 +17,13 @@ * under the License. */ +import { Readable } from 'stream'; import { ScopedSavedObjectsClientProvider } from './lib'; import { SavedObjectsClient } from './saved_objects_client'; +import { SavedObjectsExportOptions } from '../export'; +import { SavedObjectsImportOptions, SavedObjectsImportResponse } from '../import'; +import { SavedObjectsSchema } from '../schema'; +import { SavedObjectsResolveImportErrorsOptions } from '../import/types'; /** * @public @@ -31,12 +36,22 @@ export interface SavedObjectsService { getScopedSavedObjectsClient: ScopedSavedObjectsClientProvider['getClient']; SavedObjectsClient: typeof SavedObjectsClient; types: string[]; + schema: SavedObjectsSchema; getSavedObjectsRepository(...rest: any[]): any; + importExport: { + objectLimit: number; + importSavedObjects(options: SavedObjectsImportOptions): Promise; + resolveImportErrors( + options: SavedObjectsResolveImportErrorsOptions + ): Promise; + getSortedObjectsForExport(options: SavedObjectsExportOptions): Promise; + }; } export { SavedObjectsRepository, ScopedSavedObjectsClientProvider, + SavedObjectsClientProviderOptions, SavedObjectsClientWrapperFactory, SavedObjectsClientWrapperOptions, SavedObjectsErrorHelpers, diff --git a/src/core/server/saved_objects/service/lib/index.ts b/src/core/server/saved_objects/service/lib/index.ts index 19fdc3d75f603..d987737c2ffa0 100644 --- a/src/core/server/saved_objects/service/lib/index.ts +++ b/src/core/server/saved_objects/service/lib/index.ts @@ -22,6 +22,7 @@ export { SavedObjectsClientWrapperFactory, SavedObjectsClientWrapperOptions, ScopedSavedObjectsClientProvider, + SavedObjectsClientProviderOptions, } from './scoped_client_provider'; export { SavedObjectsErrorHelpers } from './errors'; diff --git a/src/core/server/saved_objects/service/lib/scoped_client_provider.ts b/src/core/server/saved_objects/service/lib/scoped_client_provider.ts index fc0a3ea64c7a4..e0ca16e254e18 100644 --- a/src/core/server/saved_objects/service/lib/scoped_client_provider.ts +++ b/src/core/server/saved_objects/service/lib/scoped_client_provider.ts @@ -19,21 +19,37 @@ import { PriorityCollection } from './priority_collection'; import { SavedObjectsClientContract } from '..'; +/** + * Options passed to each SavedObjectsClientWrapperFactory to aid in creating the wrapper instance. + * @public + */ export interface SavedObjectsClientWrapperOptions { client: SavedObjectsClientContract; request: Request; } +/** + * Describes the factory used to create instances of Saved Objects Client Wrappers. + * @public + */ export type SavedObjectsClientWrapperFactory = ( options: SavedObjectsClientWrapperOptions ) => SavedObjectsClientContract; +/** + * Describes the factory used to create instances of the Saved Objects Client. + * @public + */ export type SavedObjectsClientFactory = ({ request, }: { request: Request; }) => SavedObjectsClientContract; +/** + * Options to control the creation of the Saved Objects Client. + * @public + */ export interface SavedObjectsClientProviderOptions { excludedWrappers?: string[]; } diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md index 6ab535a66cb36..94bed2ad01e64 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -5,13 +5,13 @@ ```ts import Boom from 'boom'; -import { ByteSizeValue } from '@kbn/config-schema'; import { CallCluster } from 'src/legacy/core_plugins/elasticsearch'; import { ConfigOptions } from 'elasticsearch'; import { Duration } from 'moment'; import { IncomingHttpHeaders } from 'http'; import { ObjectType } from '@kbn/config-schema'; import { Observable } from 'rxjs'; +import { Readable } from 'stream'; import { Request } from 'hapi'; import { ResponseObject } from 'hapi'; import { ResponseToolkit } from 'hapi'; @@ -39,6 +39,13 @@ export interface AuthResultParams { state?: Record; } +// @public +export enum AuthStatus { + authenticated = "authenticated", + unauthenticated = "unauthenticated", + unknown = "unknown" +} + // @public export interface AuthToolkit { authenticated: (data?: AuthResultParams) => AuthResult; @@ -100,7 +107,6 @@ export interface CoreSetup { registerAuth: HttpServiceSetup['registerAuth']; registerOnPostAuth: HttpServiceSetup['registerOnPostAuth']; basePath: HttpServiceSetup['basePath']; - createNewServer: HttpServiceSetup['createNewServer']; isTlsEnabled: HttpServiceSetup['isTlsEnabled']; }; } @@ -109,6 +115,12 @@ export interface CoreSetup { export interface CoreStart { } +// @public +export interface CustomHttpResponseOptions extends HttpResponseOptions { + // (undocumented) + statusCode: number; +} + // @public export interface DiscoveredPlugin { readonly configPath: ConfigPath; @@ -162,21 +174,58 @@ export interface FakeRequest { } // @public -export type GetAuthHeaders = (request: KibanaRequest | Request) => AuthHeaders | undefined; +export type GetAuthHeaders = (request: KibanaRequest | LegacyRequest) => AuthHeaders | undefined; -// @public (undocumented) -export type Headers = Record; +// @public +export type GetAuthState = (request: KibanaRequest | LegacyRequest) => { + status: AuthStatus; + state: unknown; +}; -// Warning: (ae-forgotten-export) The symbol "HttpServerSetup" needs to be exported by the entry point index.d.ts -// -// @public (undocumented) -export interface HttpServiceSetup extends HttpServerSetup { - // Warning: (ae-forgotten-export) The symbol "HttpConfig" needs to be exported by the entry point index.d.ts - // +// @public +export type Headers = { + [header in KnownHeaders]?: string | string[] | undefined; +} & { + [header: string]: string | string[] | undefined; +}; + +// @public +export interface HttpResponseOptions { + // Warning: (ae-forgotten-export) The symbol "ResponseHeaders" needs to be exported by the entry point index.d.ts + headers?: ResponseHeaders; +} + +// @public +export type HttpResponsePayload = undefined | string | Record | Buffer | Stream; + +// @public +export interface HttpServerSetup { + // (undocumented) + auth: { + get: GetAuthState; + isAuthenticated: IsAuthenticated; + getAuthHeaders: GetAuthHeaders; + }; // (undocumented) - createNewServer: (cfg: Partial) => Promise; + basePath: { + get: (request: KibanaRequest | LegacyRequest) => string; + set: (request: KibanaRequest | LegacyRequest, basePath: string) => void; + prepend: (url: string) => string; + remove: (url: string) => string; + }; + createCookieSessionStorageFactory: (cookieOptions: SessionStorageCookieOptions) => Promise>; + isTlsEnabled: boolean; + registerAuth: (handler: AuthenticationHandler) => void; + registerOnPostAuth: (handler: OnPostAuthHandler) => void; + registerOnPreAuth: (handler: OnPreAuthHandler) => void; + registerRouter: (router: Router) => void; + // (undocumented) + server: Server; } +// @public (undocumented) +export type HttpServiceSetup = HttpServerSetup; + // @public (undocumented) export interface HttpServiceStart { isListening: (port: number) => boolean; @@ -198,6 +247,9 @@ export interface InternalCoreStart { plugins: PluginsServiceStart; } +// @public +export type IsAuthenticated = (request: KibanaRequest | LegacyRequest) => boolean; + // @public export class KibanaRequest { // @internal (undocumented) @@ -214,9 +266,7 @@ export class KibanaRequest { readonly params: Params; // (undocumented) readonly query: Query; - // (undocumented) readonly route: RecursiveReadonly; - // (undocumented) readonly url: Url; } @@ -231,7 +281,37 @@ export interface KibanaRequestRoute { } // @public -export type LegacyRequest = Request; +export type KibanaResponseFactory = typeof kibanaResponseFactory; + +// @public +export const kibanaResponseFactory: { + ok: (payload: HttpResponsePayload, options?: HttpResponseOptions) => KibanaResponse | Buffer | Stream>; + accepted: (payload?: HttpResponsePayload, options?: HttpResponseOptions) => KibanaResponse | Buffer | Stream>; + noContent: (options?: HttpResponseOptions) => KibanaResponse; + custom: (payload: string | Error | Record | Buffer | Stream | { + message: string | Error; + meta?: ResponseErrorMeta | undefined; + } | undefined, options: CustomHttpResponseOptions) => KibanaResponse | Buffer | Stream | { + message: string | Error; + meta?: ResponseErrorMeta | undefined; + }>; + redirected: (payload: HttpResponsePayload, options: RedirectResponseOptions) => KibanaResponse | Buffer | Stream>; + badRequest: (error?: ResponseError, options?: HttpResponseOptions) => KibanaResponse; + unauthorized: (error?: ResponseError, options?: HttpResponseOptions) => KibanaResponse; + forbidden: (error?: ResponseError, options?: HttpResponseOptions) => KibanaResponse; + notFound: (error?: ResponseError, options?: HttpResponseOptions) => KibanaResponse; + conflict: (error?: ResponseError, options?: HttpResponseOptions) => KibanaResponse; + internal: (error?: ResponseError, options?: HttpResponseOptions) => KibanaResponse; +}; + +// Warning: (ae-forgotten-export) The symbol "KnownKeys" needs to be exported by the entry point index.d.ts +// +// @public +export type KnownHeaders = KnownKeys; + +// @public @deprecated (undocumented) +export interface LegacyRequest extends Request { +} // @public export interface Logger { @@ -387,6 +467,16 @@ export type RecursiveReadonly = T extends (...args: any[]) => any ? T : T ext [K in keyof T]: RecursiveReadonly; }> : T; +// @public +export type RedirectResponseOptions = HttpResponseOptions & { + headers: { + location: string; + }; +}; + +// @public +export type RequestHandler

= (request: KibanaRequest, TypeOf, TypeOf>, response: KibanaResponseFactory) => KibanaResponse | Promise>; + // @public export type ResponseError = string | Error | { message: string | Error; @@ -403,6 +493,13 @@ export interface ResponseErrorMeta { errorCode?: string; } +// @public +export interface RouteConfig

{ + options?: RouteConfigOptions; + path: string; + validate: RouteSchemas | false; +} + // @public export interface RouteConfigOptions { authRequired?: boolean; @@ -412,13 +509,12 @@ export interface RouteConfigOptions { // @public export type RouteMethod = 'get' | 'post' | 'put' | 'delete'; -// @public (undocumented) +// @public export class Router { constructor(path: string); delete

(route: RouteConfig, handler: RequestHandler): void; - // Warning: (ae-forgotten-export) The symbol "RouteConfig" needs to be exported by the entry point index.d.ts - // Warning: (ae-forgotten-export) The symbol "RequestHandler" needs to be exported by the entry point index.d.ts get

(route: RouteConfig, handler: RequestHandler): void; + // @internal getRoutes(): Readonly[]; // (undocumented) readonly path: string; @@ -533,14 +629,16 @@ export class SavedObjectsClient { // @public export type SavedObjectsClientContract = Pick; -// Warning: (ae-missing-release-tag) "SavedObjectsClientWrapperFactory" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public +export interface SavedObjectsClientProviderOptions { + // (undocumented) + excludedWrappers?: string[]; +} + +// @public export type SavedObjectsClientWrapperFactory = (options: SavedObjectsClientWrapperOptions) => SavedObjectsClientContract; -// Warning: (ae-missing-release-tag) "SavedObjectsClientWrapperOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export interface SavedObjectsClientWrapperOptions { // (undocumented) client: SavedObjectsClientContract; @@ -608,6 +706,25 @@ export class SavedObjectsErrorHelpers { static isSavedObjectsClientError(error: any): error is DecoratedError; } +// @public +export interface SavedObjectsExportOptions { + // (undocumented) + exportSizeLimit: number; + // (undocumented) + includeReferencesDeep?: boolean; + // (undocumented) + namespace?: string; + // (undocumented) + objects?: Array<{ + id: string; + type: string; + }>; + // (undocumented) + savedObjectsClient: SavedObjectsClientContract; + // (undocumented) + types?: string[]; +} + // @public (undocumented) export interface SavedObjectsFindOptions extends SavedObjectsBaseOptions { // (undocumented) @@ -646,6 +763,98 @@ export interface SavedObjectsFindResponse total: number; } +// @public +export interface SavedObjectsImportConflictError { + // (undocumented) + type: 'conflict'; +} + +// @public +export interface SavedObjectsImportError { + // (undocumented) + error: SavedObjectsImportConflictError | SavedObjectsImportUnsupportedTypeError | SavedObjectsImportMissingReferencesError | SavedObjectsImportUnknownError; + // (undocumented) + id: string; + // (undocumented) + title?: string; + // (undocumented) + type: string; +} + +// @public +export interface SavedObjectsImportMissingReferencesError { + // (undocumented) + blocking: Array<{ + type: string; + id: string; + }>; + // (undocumented) + references: Array<{ + type: string; + id: string; + }>; + // (undocumented) + type: 'missing_references'; +} + +// @public +export interface SavedObjectsImportOptions { + // (undocumented) + namespace?: string; + // (undocumented) + objectLimit: number; + // (undocumented) + overwrite: boolean; + // (undocumented) + readStream: Readable; + // (undocumented) + savedObjectsClient: SavedObjectsClientContract; + // (undocumented) + supportedTypes: string[]; +} + +// @public +export interface SavedObjectsImportResponse { + // (undocumented) + errors?: SavedObjectsImportError[]; + // (undocumented) + success: boolean; + // (undocumented) + successCount: number; +} + +// @public +export interface SavedObjectsImportRetry { + // (undocumented) + id: string; + // (undocumented) + overwrite: boolean; + // (undocumented) + replaceReferences: Array<{ + type: string; + from: string; + to: string; + }>; + // (undocumented) + type: string; +} + +// @public +export interface SavedObjectsImportUnknownError { + // (undocumented) + message: string; + // (undocumented) + statusCode: number; + // (undocumented) + type: 'unknown'; +} + +// @public +export interface SavedObjectsImportUnsupportedTypeError { + // (undocumented) + type: 'unsupported_type'; +} + // @public export interface SavedObjectsMigrationVersion { // (undocumented) @@ -668,6 +877,22 @@ export interface SavedObjectsRawDoc { _type?: string; } +// @public +export interface SavedObjectsResolveImportErrorsOptions { + // (undocumented) + namespace?: string; + // (undocumented) + objectLimit: number; + // (undocumented) + readStream: Readable; + // (undocumented) + retries: SavedObjectsImportRetry[]; + // (undocumented) + savedObjectsClient: SavedObjectsClientContract; + // (undocumented) + supportedTypes: string[]; +} + // Warning: (ae-missing-release-tag) "SavedObjectsSchema" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -704,11 +929,20 @@ export interface SavedObjectsService { getSavedObjectsRepository(...rest: any[]): any; // (undocumented) getScopedSavedObjectsClient: ScopedSavedObjectsClientProvider['getClient']; + // (undocumented) + importExport: { + objectLimit: number; + importSavedObjects(options: SavedObjectsImportOptions): Promise; + resolveImportErrors(options: SavedObjectsResolveImportErrorsOptions): Promise; + getSortedObjectsForExport(options: SavedObjectsExportOptions): Promise; + }; // Warning: (ae-incompatible-release-tags) The symbol "SavedObjectsClient" is marked as @public, but its signature references "SavedObjectsClient" which is marked as @internal // // (undocumented) SavedObjectsClient: typeof SavedObjectsClient; // (undocumented) + schema: SavedObjectsSchema; + // (undocumented) types: string[]; } @@ -729,7 +963,7 @@ export interface SavedObjectsUpdateResponse | undefined); + constructor(internalAPICaller: APICaller, scopedAPICaller: APICaller, headers?: Headers | undefined); callAsCurrentUser(endpoint: string, clientParams?: Record, options?: CallAPIOptions): Promise; callAsInternalUser(endpoint: string, clientParams?: Record, options?: CallAPIOptions): Promise; } @@ -741,6 +975,14 @@ export interface SessionStorage { set(sessionValue: T): void; } +// @public +export interface SessionStorageCookieOptions { + encryptionKey: string; + isSecure: boolean; + name: string; + validate: (sessionValue: T) => boolean | Promise; +} + // @public export interface SessionStorageFactory { // (undocumented) @@ -750,6 +992,7 @@ export interface SessionStorageFactory { // Warnings were encountered during analysis: // +// src/core/server/http/router/response.ts:188:3 - (ae-forgotten-export) The symbol "KibanaResponse" needs to be exported by the entry point index.d.ts // src/core/server/plugins/plugin_context.ts:34:10 - (ae-forgotten-export) The symbol "EnvironmentMode" needs to be exported by the entry point index.d.ts // src/core/server/plugins/plugins_service.ts:37:5 - (ae-forgotten-export) The symbol "DiscoveredPluginInternal" needs to be exported by the entry point index.d.ts diff --git a/src/dev/build/tasks/optimize_task.js b/src/dev/build/tasks/optimize_task.js index f6de0c717abfe..bbd8f9622535d 100644 --- a/src/dev/build/tasks/optimize_task.js +++ b/src/dev/build/tasks/optimize_task.js @@ -49,7 +49,7 @@ export const OptimizeBuildTask = { env: { FORCE_DLL_CREATION: 'true', KBN_CACHE_LOADER_WRITABLE: 'true', - NODE_OPTIONS: '--max-old-space-size=2048' + NODE_OPTIONS: '--max-old-space-size=3072' }, }); diff --git a/src/dev/jest/setup/polyfills.js b/src/dev/jest/setup/polyfills.js index 293fbcf3616d7..9394de0aea936 100644 --- a/src/dev/jest/setup/polyfills.js +++ b/src/dev/jest/setup/polyfills.js @@ -23,17 +23,6 @@ const bluebird = require('bluebird'); bluebird.Promise.setScheduler(function (fn) { global.setImmediate.call(global, fn); }); const MutationObserver = require('mutation-observer'); -// There's a bug in mutation-observer around the `attributes` option -// https://dom.spec.whatwg.org/#mutationobserver -// If either options's attributeOldValue or attributeFilter is present and options's attributes is omitted, then set options's attributes to true. -const _observe = MutationObserver.prototype.observe; -MutationObserver.prototype.observe = function observe(target, options) { - const needsAttributes = options.hasOwnProperty('attributeOldValue') || options.hasOwnProperty('attributeFilter'); - if (needsAttributes && !options.hasOwnProperty('attributes')) { - options.attributes = true; - } - Function.prototype.call(_observe, this, target, options); -}; Object.defineProperty(window, 'MutationObserver', { value: MutationObserver }); require('whatwg-fetch'); diff --git a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_core.test.mocks.ts b/src/legacy/core_plugins/dashboard_embeddable_container/public/np_core.test.mocks.ts index a3909bc556b57..1e71e2b26970b 100644 --- a/src/legacy/core_plugins/dashboard_embeddable_container/public/np_core.test.mocks.ts +++ b/src/legacy/core_plugins/dashboard_embeddable_container/public/np_core.test.mocks.ts @@ -17,35 +17,11 @@ * under the License. */ -import { fatalErrorsServiceMock, notificationServiceMock } from '../../../../core/public/mocks'; - let modalContents: React.Component; export const getModalContents = () => modalContents; -jest.doMock('ui/new_platform', () => { - return { - npStart: { - core: { - overlays: { - openFlyout: jest.fn(), - openModal: (component: React.Component) => { - modalContents = component; - return { - close: jest.fn(), - }; - }, - }, - }, - }, - npSetup: { - core: { - fatalErrors: fatalErrorsServiceMock.createSetupContract(), - notifications: notificationServiceMock.createSetupContract(), - }, - }, - }; -}); +jest.mock('ui/new_platform'); jest.doMock('ui/metadata', () => ({ metadata: { diff --git a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_bar.tsx b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_bar.tsx index 1f115964709e9..ca2b26cc3202d 100644 --- a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_bar.tsx +++ b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_bar.tsx @@ -31,20 +31,19 @@ import { import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react'; import classNames from 'classnames'; import React, { Component } from 'react'; -import chrome from 'ui/chrome'; +import { UiSettingsClientContract } from 'src/core/public'; import { IndexPattern } from '../../index_patterns'; import { FilterEditor } from './filter_editor'; import { FilterItem } from './filter_item'; import { FilterOptions } from './filter_options'; -const config = chrome.getUiSettingsClient(); - interface Props { filters: Filter[]; onFiltersUpdated: (filters: Filter[]) => void; className: string; indexPatterns: IndexPattern[]; intl: InjectedIntl; + uiSettings: UiSettingsClientContract; } interface State { @@ -103,13 +102,14 @@ class FilterBarUI extends Component { onUpdate={newFilter => this.onUpdate(i, newFilter)} onRemove={() => this.onRemove(i)} indexPatterns={this.props.indexPatterns} + uiSettings={this.props.uiSettings} /> )); } private renderAddFilter() { - const isPinned = config.get('filters:pinnedByDefault'); + const isPinned = this.props.uiSettings.get('filters:pinnedByDefault'); const [indexPattern] = this.props.indexPatterns; const index = indexPattern && indexPattern.id; const newFilter = buildEmptyFilter(isPinned, index); @@ -144,6 +144,7 @@ class FilterBarUI extends Component { onSubmit={this.onAdd} onCancel={this.onCloseAddFilterPopover} key={JSON.stringify(newFilter)} + uiSettings={this.props.uiSettings} /> diff --git a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/index.tsx b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/index.tsx index 1bb594c183bc6..0d46e9e7bac63 100644 --- a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/index.tsx +++ b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/index.tsx @@ -36,6 +36,7 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react'; import { get } from 'lodash'; import React, { Component } from 'react'; +import { UiSettingsClientContract } from 'src/core/public'; import { Field, IndexPattern } from '../../../index_patterns'; import { GenericComboBox, GenericComboBoxProps } from './generic_combo_box'; import { @@ -61,6 +62,7 @@ interface Props { onSubmit: (filter: Filter) => void; onCancel: () => void; intl: InjectedIntl; + uiSettings: UiSettingsClientContract; } interface State { @@ -327,6 +329,7 @@ class FilterEditorUI extends Component { value={this.state.params} onChange={this.onParamsChange} data-test-subj="phraseValueInput" + uiSettings={this.props.uiSettings} /> ); case 'phrases': @@ -336,6 +339,7 @@ class FilterEditorUI extends Component { field={this.state.selectedField} values={this.state.params} onChange={this.onParamsChange} + uiSettings={this.props.uiSettings} /> ); case 'range': diff --git a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/phrase_suggestor.tsx b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/phrase_suggestor.tsx index 9cfd3aa4fd331..6b262c66402f2 100644 --- a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/phrase_suggestor.tsx +++ b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/phrase_suggestor.tsx @@ -18,14 +18,14 @@ */ import { Component } from 'react'; -import chrome from 'ui/chrome'; import { getSuggestions } from 'ui/value_suggestions'; +import { UiSettingsClientContract } from 'src/core/public'; import { Field, IndexPattern } from '../../../index_patterns'; -const config = chrome.getUiSettingsClient(); export interface PhraseSuggestorProps { indexPattern: IndexPattern; field?: Field; + uiSettings: UiSettingsClientContract; } export interface PhraseSuggestorState { @@ -52,7 +52,7 @@ export class PhraseSuggestor extends Component< } protected isSuggestingValues() { - const shouldSuggestValues = config.get('filterEditor:suggestValues'); + const shouldSuggestValues = this.props.uiSettings.get('filterEditor:suggestValues'); const { field } = this.props; return shouldSuggestValues && field && field.aggregatable && field.type === 'string'; } diff --git a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_item.tsx b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_item.tsx index 453dafdb29fe3..ee8d660e44f55 100644 --- a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_item.tsx +++ b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_item.tsx @@ -28,6 +28,7 @@ import { import { InjectedIntl, injectI18n } from '@kbn/i18n/react'; import classNames from 'classnames'; import React, { Component } from 'react'; +import { UiSettingsClientContract } from 'src/core/public'; import { IndexPattern } from '../../index_patterns'; import { FilterEditor } from './filter_editor'; import { FilterView } from './filter_view'; @@ -40,6 +41,7 @@ interface Props { onUpdate: (filter: Filter) => void; onRemove: () => void; intl: InjectedIntl; + uiSettings: UiSettingsClientContract; } interface State { @@ -169,6 +171,7 @@ class FilterItemUI extends Component { indexPatterns={this.props.indexPatterns} onSubmit={this.onSubmit} onCancel={this.closePopover} + uiSettings={this.props.uiSettings} /> ), diff --git a/src/legacy/core_plugins/data/public/filter/filter_manager/filter_manager.test.ts b/src/legacy/core_plugins/data/public/filter/filter_manager/filter_manager.test.ts index 3f3cbd0044a07..8fba16037ed97 100644 --- a/src/legacy/core_plugins/data/public/filter/filter_manager/filter_manager.test.ts +++ b/src/legacy/core_plugins/data/public/filter/filter_manager/filter_manager.test.ts @@ -32,35 +32,20 @@ import { StubIndexPatterns } from './test_helpers/stub_index_pattern'; import { StubState } from './test_helpers/stub_state'; import { getFiltersArray } from './test_helpers/get_filters_array'; -jest.mock( - 'ui/chrome', - () => ({ - getBasePath: jest.fn(() => 'path'), - getUiSettingsClient: jest.fn(() => { - return { - get: () => true, - }; - }), - }), - { virtual: true } -); - -jest.mock('ui/new_platform', () => ({ - npStart: { - core: { - chrome: { - recentlyAccessed: false, - }, - }, - }, - npSetup: { - core: { - uiSettings: { - get: () => true, - }, +import { coreMock } from '../../../../../../core/public/mocks'; +const setupMock = coreMock.createSetup(); + +setupMock.uiSettings.get.mockImplementation((key: string) => { + return true; +}); + +jest.mock('ui/timefilter', () => { + return { + timefilter: { + setTime: jest.fn(), }, - }, -})); + }; +}); describe('filter_manager', () => { let appStateStub: StubState; @@ -79,7 +64,7 @@ describe('filter_manager', () => { appStateStub = new StubState(); globalStateStub = new StubState(); indexPatterns = new StubIndexPatterns(); - filterManager = new FilterManager(indexPatterns as IndexPatterns); + filterManager = new FilterManager(indexPatterns as IndexPatterns, setupMock.uiSettings); readyFilters = getFiltersArray(); // FilterStateManager is tested indirectly. diff --git a/src/legacy/core_plugins/data/public/filter/filter_manager/filter_manager.ts b/src/legacy/core_plugins/data/public/filter/filter_manager/filter_manager.ts index ccb26c801c701..f230b035de352 100644 --- a/src/legacy/core_plugins/data/public/filter/filter_manager/filter_manager.ts +++ b/src/legacy/core_plugins/data/public/filter/filter_manager/filter_manager.ts @@ -22,8 +22,7 @@ import { Filter, isFilterPinned, FilterStateStore } from '@kbn/es-query'; import _ from 'lodash'; import { Subject } from 'rxjs'; -import { npSetup } from 'ui/new_platform'; - +import { UiSettingsClientContract } from 'src/core/public'; // @ts-ignore import { compareFilters } from './lib/compare_filters'; // @ts-ignore @@ -46,9 +45,11 @@ export class FilterManager { private filters: Filter[] = []; private updated$: Subject = new Subject(); private fetch$: Subject = new Subject(); + private uiSettings: UiSettingsClientContract; - constructor(indexPatterns: IndexPatterns) { + constructor(indexPatterns: IndexPatterns, uiSettings: UiSettingsClientContract) { this.indexPatterns = indexPatterns; + this.uiSettings = uiSettings; } private mergeIncomingFilters(partitionedFilters: PartitionedFilters): Filter[] { @@ -144,9 +145,8 @@ export class FilterManager { return; } - const { uiSettings } = npSetup.core; if (pinFilterStatus === undefined) { - pinFilterStatus = uiSettings.get('filters:pinnedByDefault'); + pinFilterStatus = this.uiSettings.get('filters:pinnedByDefault'); } // Set the store of all filters. For now. diff --git a/src/legacy/core_plugins/data/public/filter/filter_manager/filter_state_manager.test.ts b/src/legacy/core_plugins/data/public/filter/filter_manager/filter_state_manager.test.ts index 4152a0931b031..778663821f55b 100644 --- a/src/legacy/core_plugins/data/public/filter/filter_manager/filter_state_manager.test.ts +++ b/src/legacy/core_plugins/data/public/filter/filter_manager/filter_state_manager.test.ts @@ -28,22 +28,20 @@ import { getFilter } from './test_helpers/get_stub_filter'; import { FilterManager } from './filter_manager'; import { StubIndexPatterns } from './test_helpers/stub_index_pattern'; -jest.mock('ui/new_platform', () => ({ - npStart: { - core: { - chrome: { - recentlyAccessed: false, - }, - }, - }, - npSetup: { - core: { - uiSettings: { - get: () => true, - }, +import { coreMock } from '../../../../../../core/public/mocks'; +const setupMock = coreMock.createSetup(); + +setupMock.uiSettings.get.mockImplementation((key: string) => { + return true; +}); + +jest.mock('ui/timefilter', () => { + return { + timefilter: { + setTime: jest.fn(), }, - }, -})); + }; +}); describe('filter_state_manager', () => { let appStateStub: StubState; @@ -55,7 +53,7 @@ describe('filter_state_manager', () => { appStateStub = new StubState(); globalStateStub = new StubState(); const indexPatterns = new StubIndexPatterns(); - filterManager = new FilterManager(indexPatterns as IndexPatterns); + filterManager = new FilterManager(indexPatterns as IndexPatterns, setupMock.uiSettings); }); describe('app_state_undefined', () => { diff --git a/src/legacy/core_plugins/data/public/filter/filter_service.ts b/src/legacy/core_plugins/data/public/filter/filter_service.ts index 27cf027e45907..7be9f4f04b48f 100644 --- a/src/legacy/core_plugins/data/public/filter/filter_service.ts +++ b/src/legacy/core_plugins/data/public/filter/filter_service.ts @@ -17,6 +17,7 @@ * under the License. */ +import { UiSettingsClientContract } from 'src/core/public'; import { IndexPatterns } from '../index_patterns'; import { FilterManager } from './filter_manager'; /** @@ -26,11 +27,12 @@ import { FilterManager } from './filter_manager'; export interface FilterServiceDependencies { indexPatterns: IndexPatterns; + uiSettings: UiSettingsClientContract; } export class FilterService { - public setup({ indexPatterns }: FilterServiceDependencies) { - const filterManager = new FilterManager(indexPatterns); + public setup({ indexPatterns, uiSettings }: FilterServiceDependencies) { + const filterManager = new FilterManager(indexPatterns, uiSettings); return { filterManager, diff --git a/src/legacy/core_plugins/data/public/index.ts b/src/legacy/core_plugins/data/public/index.ts index ef91679ce2f37..6d33349614294 100644 --- a/src/legacy/core_plugins/data/public/index.ts +++ b/src/legacy/core_plugins/data/public/index.ts @@ -33,6 +33,7 @@ export { ExpressionRenderer, ExpressionRendererProps, ExpressionRunner } from '. export { IndexPattern, IndexPatterns, StaticIndexPattern, Field } from './index_patterns'; export { Query, QueryBar, QueryBarInput } from './query'; export { FilterBar, ApplyFiltersPopover } from './filter'; +export { SearchBar, SearchBarProps } from './search'; export { FilterManager, FilterStateManager, diff --git a/src/legacy/core_plugins/data/public/index_patterns/index_patterns_service.ts b/src/legacy/core_plugins/data/public/index_patterns/index_patterns_service.ts index 74918bebccf08..731361ee10262 100644 --- a/src/legacy/core_plugins/data/public/index_patterns/index_patterns_service.ts +++ b/src/legacy/core_plugins/data/public/index_patterns/index_patterns_service.ts @@ -16,8 +16,6 @@ * specific language governing permissions and limitations * under the License. */ - -import chrome from 'ui/chrome'; // @ts-ignore import { mockFields, mockIndexPattern } from 'ui/index_patterns/fixtures'; // @ts-ignore @@ -35,9 +33,15 @@ import { isFilterable, getFromSavedObject } from 'ui/index_patterns/static_utils // IndexPattern, StaticIndexPattern, Field import * as types from 'ui/index_patterns'; +import { SavedObjectsClient } from 'ui/saved_objects'; + +import { UiSettingsClientContract } from 'src/core/public'; + +export interface IndexPatternDependencies { + uiSettings: UiSettingsClientContract; + savedObjectsClient: SavedObjectsClient; +} -const config = chrome.getUiSettingsClient(); -const savedObjectsClient = chrome.getSavedObjectsClient(); /** * Index Patterns Service * @@ -50,9 +54,9 @@ const savedObjectsClient = chrome.getSavedObjectsClient(); * @internal */ export class IndexPatternsService { - public setup() { + public setup({ uiSettings, savedObjectsClient }: IndexPatternDependencies) { return { - indexPatterns: new IndexPatterns(config, savedObjectsClient), + indexPatterns: new IndexPatterns(uiSettings, savedObjectsClient), }; } diff --git a/src/legacy/core_plugins/data/public/plugin.ts b/src/legacy/core_plugins/data/public/plugin.ts index 31f2596dd2182..34edb355d2ef8 100644 --- a/src/legacy/core_plugins/data/public/plugin.ts +++ b/src/legacy/core_plugins/data/public/plugin.ts @@ -19,6 +19,7 @@ import { CoreSetup, CoreStart, Plugin } from '../../../../core/public'; import { ExpressionsService, ExpressionsSetup } from './expressions'; +import { SearchService, SearchSetup } from './search'; import { QueryService, QuerySetup } from './query'; import { FilterService, FilterSetup } from './filter'; import { IndexPatternsService, IndexPatternsSetup } from './index_patterns'; @@ -44,6 +45,7 @@ export interface DataSetup { indexPatterns: IndexPatternsSetup; filter: FilterSetup; query: QuerySetup; + search: SearchSetup; } /** @@ -63,18 +65,27 @@ export class DataPlugin implements Plugin - - - - - - - - - - - - - - -`; diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/__snapshots__/query_bar_input.test.tsx.snap b/src/legacy/core_plugins/data/public/query/query_bar/components/__snapshots__/query_bar_input.test.tsx.snap index 00576d05bdcac..b13916a3e5b29 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/__snapshots__/query_bar_input.test.tsx.snap +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/__snapshots__/query_bar_input.test.tsx.snap @@ -150,6 +150,36 @@ exports[`QueryBarInput Should disable autoFocus on EuiFieldText when disableAuto }, } } + uiSettings={ + Object { + "get": [MockFunction] { + "calls": Array [ + Array [ + "history:limit", + ], + ], + "results": Array [ + Object { + "type": "return", + "value": undefined, + }, + ], + }, + "get$": [MockFunction], + "getAll": [MockFunction], + "getSaved$": [MockFunction], + "getUpdate$": [MockFunction], + "getUpdateErrors$": [MockFunction], + "isCustom": [MockFunction], + "isDeclared": [MockFunction], + "isDefault": [MockFunction], + "isOverridden": [MockFunction], + "overrideLocalDefault": [MockFunction], + "remove": [MockFunction], + "set": [MockFunction], + "stop": [MockFunction], + } + } > { + switch (key) { + case 'timepicker:quickRanges': + return [ + { + from: 'now/d', + to: 'now/d', + display: 'Today', + }, + ]; + case 'dateFormat': + return 'YY'; + case 'history:limit': + return 10; + default: + throw new Error(`Unexpected config key: ${key}`); + } +}); + const noop = () => { return; }; @@ -76,6 +98,7 @@ describe('QueryBar', () => { it('Should render the given query', () => { const component = shallowWithIntl( { /> ); - expect(component).toMatchSnapshot(); + expect(component.find(QUERY_INPUT_SELECTOR).length).toBe(1); + expect(component.find(TIMEPICKER_SELECTOR).length).toBe(1); }); it('Should create a unique PersistedLog based on the appName and query language', () => { shallowWithIntl( { it('Should render only timepicker when no options provided', () => { const component = shallowWithIntl( { it('Should not show timepicker when asked', () => { const component = shallowWithIntl( { it('Should render timepicker with options', () => { const component = shallowWithIntl( { it('Should render only query input bar', () => { const component = shallowWithIntl( { it('Should NOT render query input bar if disabled', () => { const component = shallowWithIntl( { it('Should NOT render query input bar if missing options', () => { const component = shallowWithIntl( void; customSubmitButton?: any; + uiSettings: UiSettingsClientContract; } interface State { @@ -243,13 +242,21 @@ export class QueryBarUI extends Component { public componentDidMount() { if (!this.props.query) return; - this.persistedLog = getQueryLog(this.props.appName, this.props.query.language); + this.persistedLog = getQueryLog( + this.props.uiSettings, + this.props.appName, + this.props.query.language + ); } public componentDidUpdate(prevProps: Props) { if (!this.props.query || !prevProps.query) return; if (prevProps.query.language !== this.props.query.language) { - this.persistedLog = getQueryLog(this.props.appName, this.props.query.language); + this.persistedLog = getQueryLog( + this.props.uiSettings, + this.props.appName, + this.props.query.language + ); } } @@ -281,6 +288,7 @@ export class QueryBarUI extends Component { onChange={this.onChange} onSubmit={this.onInputSubmit} persistedLog={this.persistedLog} + uiSettings={this.props.uiSettings} /> ); @@ -332,7 +340,7 @@ export class QueryBarUI extends Component { }; }); - const commonlyUsedRanges = config + const commonlyUsedRanges = this.props.uiSettings .get('timepicker:quickRanges') .map(({ from, to, display }: { from: string; to: string; display: string }) => { return { @@ -354,7 +362,7 @@ export class QueryBarUI extends Component { showUpdateButton={false} recentlyUsedRanges={recentlyUsedRanges} commonlyUsedRanges={commonlyUsedRanges} - dateFormat={config.get('dateFormat')} + dateFormat={this.props.uiSettings.get('dateFormat')} isAutoRefreshOnly={this.props.showAutoRefreshOnly} /> diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.test.mocks.ts b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.test.mocks.ts index b2f308127b0e1..7bdc9a2448363 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.test.mocks.ts +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.test.mocks.ts @@ -35,34 +35,6 @@ const mockIndexPattern = { ], }; -const mockChromeFactory = jest.fn(() => { - return { - getBasePath: () => `foo`, - getUiSettingsClient: () => { - return { - get: (key: string) => { - switch (key) { - case 'timepicker:quickRanges': - return [ - { - from: 'now/d', - to: 'now/d', - display: 'Today', - }, - ]; - case 'dateFormat': - return 'YY'; - case 'history:limit': - return 10; - default: - throw new Error(`Unexpected config key: ${key}`); - } - }, - }; - }, - }; -}); - export const mockPersistedLog = { add: jest.fn(), get: jest.fn(() => ['response:200']), @@ -81,16 +53,9 @@ export const mockFetchIndexPatterns = jest .fn() .mockReturnValue(Promise.resolve([mockIndexPattern])); -jest.mock('ui/chrome', () => mockChromeFactory()); -jest.mock('ui/kfetch', () => ({ - kfetch: () => {}, -})); jest.mock('ui/persisted_log', () => ({ PersistedLog: mockPersistedLogFactory, })); -jest.mock('ui/autocomplete_providers', () => ({ - getAutocompleteProvider: mockGetAutocompleteProvider, -})); jest.mock('ui/kfetch', () => ({ kfetch: mockKfetch, })); diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.test.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.test.tsx index ff155dfccdac2..d04c6032980ff 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.test.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.test.tsx @@ -28,6 +28,8 @@ import React from 'react'; import { mountWithIntl } from 'test_utils/enzyme_helpers'; import { QueryLanguageSwitcher } from './language_switcher'; import { QueryBarInput, QueryBarInputUI } from './query_bar_input'; +import { coreMock } from '../../../../../../../core/public/mocks'; +const setupMock = coreMock.createSetup(); import { IndexPattern } from '../../../index'; const noop = () => { @@ -91,6 +93,7 @@ describe('QueryBarInput', () => { indexPatterns={[mockIndexPattern]} store={createMockStorage()} intl={null as any} + uiSettings={setupMock.uiSettings} /> ); @@ -107,6 +110,7 @@ describe('QueryBarInput', () => { indexPatterns={[mockIndexPattern]} store={createMockStorage()} intl={null as any} + uiSettings={setupMock.uiSettings} /> ); @@ -124,6 +128,7 @@ describe('QueryBarInput', () => { store={createMockStorage()} disableAutoFocus={true} intl={null as any} + uiSettings={setupMock.uiSettings} /> ); @@ -143,6 +148,7 @@ describe('QueryBarInput', () => { store={createMockStorage()} disableAutoFocus={true} intl={null as any} + uiSettings={setupMock.uiSettings} /> ); @@ -163,6 +169,7 @@ describe('QueryBarInput', () => { store={mockStorage} disableAutoFocus={true} intl={null as any} + uiSettings={setupMock.uiSettings} /> ); @@ -187,6 +194,7 @@ describe('QueryBarInput', () => { store={createMockStorage()} disableAutoFocus={true} intl={null as any} + uiSettings={setupMock.uiSettings} /> ); @@ -210,6 +218,7 @@ describe('QueryBarInput', () => { store={createMockStorage()} disableAutoFocus={true} intl={null as any} + uiSettings={setupMock.uiSettings} /> ); @@ -238,8 +247,9 @@ describe('QueryBarInput', () => { store={createMockStorage()} disableAutoFocus={true} intl={null as any} + uiSettings={setupMock.uiSettings} /> ); - expect(mockFetchIndexPatterns).toHaveBeenCalledWith(['logstash-*']); + expect(mockFetchIndexPatterns).toHaveBeenCalledWith(['logstash-*'], setupMock.uiSettings); }); }); diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx index 9bfdc68763721..50628a316209a 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx @@ -30,10 +30,9 @@ import { } from 'ui/autocomplete_providers'; import { debounce, compact, isEqual, omit } from 'lodash'; import { PersistedLog } from 'ui/persisted_log'; -import chrome from 'ui/chrome'; import { kfetch } from 'ui/kfetch'; import { Storage } from 'ui/storage'; -import { localStorage } from 'ui/storage/storage_service'; +import { UiSettingsClientContract } from 'src/core/public'; import { IndexPattern, StaticIndexPattern } from '../../../index_patterns'; import { Query } from '../index'; import { fromUser, matchPairs, toUser } from '../lib'; @@ -43,14 +42,15 @@ import { getQueryLog } from '../lib/get_query_log'; import { fetchIndexPatterns } from '../lib/fetch_index_patterns'; interface Props { + uiSettings: UiSettingsClientContract; indexPatterns: Array; + store: Storage; intl: InjectedIntl; query: Query; appName: string; disableAutoFocus?: boolean; screenTitle?: string; prepend?: any; - store?: Storage; persistedLog?: PersistedLog; bubbleSubmitEvent?: boolean; languageSwitcherPopoverAnchorPosition?: PopoverAnchorPosition; @@ -80,7 +80,6 @@ const KEY_CODES = { END: 35, }; -const config = chrome.getUiSettingsClient(); const recentSearchType: AutocompleteSuggestionType = 'recentSearch'; export class QueryBarInputUI extends Component { @@ -111,7 +110,10 @@ export class QueryBarInputUI extends Component { indexPattern => typeof indexPattern !== 'string' ) as IndexPattern[]; - const objectPatternsFromStrings = (await fetchIndexPatterns(stringPatterns)) as IndexPattern[]; + const objectPatternsFromStrings = (await fetchIndexPatterns( + stringPatterns, + this.props.uiSettings + )) as IndexPattern[]; this.setState({ indexPatterns: [...objectPatterns, ...objectPatternsFromStrings], @@ -123,6 +125,7 @@ export class QueryBarInputUI extends Component { return; } + const uiSettings = this.props.uiSettings; const language = this.props.query.language; const queryString = this.getQueryString(); @@ -138,7 +141,10 @@ export class QueryBarInputUI extends Component { } const indexPatterns = this.state.indexPatterns; - const getAutocompleteSuggestions = autocompleteProvider({ config, indexPatterns }); + const getAutocompleteSuggestions = autocompleteProvider({ + config: uiSettings, + indexPatterns, + }); const { selectionStart, selectionEnd } = this.inputRef; if (selectionStart === null || selectionEnd === null) { @@ -359,11 +365,7 @@ export class QueryBarInputUI extends Component { body: JSON.stringify({ opt_in: language === 'kuery' }), }); - if (this.props.store) { - this.props.store.set('kibana.userQueryLanguage', language); - } else { - localStorage.set('kibana.userQueryLanguage', language); - } + this.props.store.set('kibana.userQueryLanguage', language); const newQuery = { query: '', language }; this.onChange(newQuery); @@ -391,7 +393,7 @@ export class QueryBarInputUI extends Component { public componentDidMount() { this.persistedLog = this.props.persistedLog ? this.props.persistedLog - : getQueryLog(this.props.appName, this.props.query.language); + : getQueryLog(this.props.uiSettings, this.props.appName, this.props.query.language); this.fetchIndexPatterns().then(this.updateSuggestions); } @@ -399,7 +401,7 @@ export class QueryBarInputUI extends Component { public componentDidUpdate(prevProps: Props) { this.persistedLog = this.props.persistedLog ? this.props.persistedLog - : getQueryLog(this.props.appName, this.props.query.language); + : getQueryLog(this.props.uiSettings, this.props.appName, this.props.query.language); if (!isEqual(prevProps.indexPatterns, this.props.indexPatterns)) { this.fetchIndexPatterns().then(this.updateSuggestions); @@ -440,6 +442,7 @@ export class QueryBarInputUI extends Component { 'languageSwitcherPopoverAnchorPosition', 'onChange', 'onSubmit', + 'uiSettings', ]); return ( diff --git a/src/legacy/core_plugins/data/public/query/query_bar/lib/fetch_index_patterns.ts b/src/legacy/core_plugins/data/public/query/query_bar/lib/fetch_index_patterns.ts index 3ba621a8f3d3f..5796623e31f53 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/lib/fetch_index_patterns.ts +++ b/src/legacy/core_plugins/data/public/query/query_bar/lib/fetch_index_patterns.ts @@ -19,11 +19,13 @@ import chrome from 'ui/chrome'; import { isEmpty } from 'lodash'; +import { UiSettingsClientContract } from 'src/core/public'; import { utils as indexPatternUtils } from '../../../index_patterns'; -const config = chrome.getUiSettingsClient(); - -export async function fetchIndexPatterns(indexPatternStrings: string[]) { +export async function fetchIndexPatterns( + indexPatternStrings: string[], + uiSettings: UiSettingsClientContract +) { if (!indexPatternStrings || isEmpty(indexPatternStrings)) { return []; } @@ -40,15 +42,17 @@ export async function fetchIndexPatterns(indexPatternStrings: string[]) { return indexPatternStrings.includes(savedObject.attributes.title as string); }); + const defaultIndex = uiSettings.get('defaultIndex'); + const allMatches = exactMatches.length === indexPatternStrings.length ? exactMatches - : [...exactMatches, await fetchDefaultIndexPattern()]; + : [...exactMatches, await fetchDefaultIndexPattern(defaultIndex)]; return allMatches.map(indexPatternUtils.getFromSavedObject); } -const fetchDefaultIndexPattern = async () => { +const fetchDefaultIndexPattern = async (defaultIndex: string) => { const savedObjectsClient = chrome.getSavedObjectsClient(); - return await savedObjectsClient.get('index-pattern', config.get('defaultIndex')); + return await savedObjectsClient.get('index-pattern', defaultIndex); }; diff --git a/src/legacy/core_plugins/data/public/query/query_bar/lib/get_query_log.ts b/src/legacy/core_plugins/data/public/query/query_bar/lib/get_query_log.ts index a3b0fe891ff9f..70dc1d3fe700e 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/lib/get_query_log.ts +++ b/src/legacy/core_plugins/data/public/query/query_bar/lib/get_query_log.ts @@ -17,14 +17,16 @@ * under the License. */ -import chrome from 'ui/chrome'; import { PersistedLog } from 'ui/persisted_log'; +import { UiSettingsClientContract } from 'src/core/public'; -const config = chrome.getUiSettingsClient(); - -export function getQueryLog(appName: string, language: string) { +export function getQueryLog( + uiSettings: UiSettingsClientContract, + appName: string, + language: string +) { return new PersistedLog(`typeahead:${appName}-${language}`, { - maxLength: config.get('history:limit'), + maxLength: uiSettings.get('history:limit'), filterDuplicates: true, }); } diff --git a/test/types/index.ts b/src/legacy/core_plugins/data/public/search/index.ts similarity index 89% rename from test/types/index.ts rename to src/legacy/core_plugins/data/public/search/index.ts index edfaeb5743524..6a9687ba7e325 100644 --- a/test/types/index.ts +++ b/src/legacy/core_plugins/data/public/search/index.ts @@ -17,4 +17,6 @@ * under the License. */ -export * from './mocha_decorations'; +export { SearchService, SearchSetup } from './search_service'; + +export * from './search_bar'; diff --git a/src/legacy/core_plugins/kibana_react/public/search_bar/components/index.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/index.tsx similarity index 100% rename from src/legacy/core_plugins/kibana_react/public/search_bar/components/index.tsx rename to src/legacy/core_plugins/data/public/search/search_bar/components/index.tsx diff --git a/src/legacy/core_plugins/kibana_react/public/search_bar/components/search_bar.test.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.test.tsx similarity index 92% rename from src/legacy/core_plugins/kibana_react/public/search_bar/components/search_bar.test.tsx rename to src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.test.tsx index bd00e35a40491..5026bd04afded 100644 --- a/src/legacy/core_plugins/kibana_react/public/search_bar/components/search_bar.test.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.test.tsx @@ -22,7 +22,10 @@ import { mountWithIntl } from 'test_utils/enzyme_helpers'; import { SearchBar } from './search_bar'; import { IndexPattern } from 'ui/index_patterns'; -jest.mock('../../../../data/public', () => { +import { coreMock } from '../../../../../../../../src/core/public/mocks'; +const setupMock = coreMock.createSetup(); + +jest.mock('../../../../../data/public', () => { return { FilterBar: () =>

, QueryBar: () =>
, @@ -80,6 +83,7 @@ describe('SearchBar', () => { it('Should render query bar when no options provided (in reality - timepicker)', () => { const component = mountWithIntl( { it('Should render empty when timepicker is off and no options provided', () => { const component = mountWithIntl( { it('Should render filter bar, when required fields are provided', () => { const component = mountWithIntl( { it('Should NOT render filter bar, if disabled', () => { const component = mountWithIntl( { it('Should render query bar, when required fields are provided', () => { const component = mountWithIntl( { it('Should NOT render query bar, if disabled', () => { const component = mountWithIntl( { it('Should render query bar and filter bar', () => { const component = mountWithIntl( { const filterCount = this.getFilterLength(); const filtersAppliedText = this.props.intl.formatMessage( { - id: 'kibana_react.search.searchBar.filtersButtonFiltersAppliedTitle', + id: 'data.search.searchBar.searchBar.filtersButtonFiltersAppliedTitle', defaultMessage: '{filterCount} {filterCount, plural, one {filter} other {filters}} applied.', }, @@ -121,11 +123,11 @@ class SearchBarUI extends Component { ); const clickToShowOrHideText = this.state.isFiltersVisible ? this.props.intl.formatMessage({ - id: 'kibana_react.search.searchBar.filtersButtonClickToShowTitle', + id: 'data.search.searchBar.searchBar.filtersButtonClickToShowTitle', defaultMessage: 'Select to hide', }) : this.props.intl.formatMessage({ - id: 'kibana_react.search.searchBar.filtersButtonClickToHideTitle', + id: 'data.search.searchBar.searchBar.filtersButtonClickToHideTitle', defaultMessage: 'Select to show', }); @@ -139,7 +141,7 @@ class SearchBarUI extends Component { aria-expanded={!!this.state.isFiltersVisible} title={`${filterCount ? filtersAppliedText : ''} ${clickToShowOrHideText}`} > - {i18n.translate('kibana_react.search.searchBar.filtersButtonLabel', { + {i18n.translate('data.search.searchBar.searchBar.filtersButtonLabel', { defaultMessage: 'Filters', description: 'The noun "filter" in plural.', })} @@ -183,10 +185,15 @@ class SearchBarUI extends Component { } public render() { + if (!this.props.uiSettings) { + return null; + } + let queryBar; if (this.shouldRenderQueryBar()) { queryBar = ( { > ; diff --git a/src/legacy/core_plugins/data/public/shim/legacy_module.ts b/src/legacy/core_plugins/data/public/shim/legacy_module.ts index 46849196e9707..aab4eb296ec56 100644 --- a/src/legacy/core_plugins/data/public/shim/legacy_module.ts +++ b/src/legacy/core_plugins/data/public/shim/legacy_module.ts @@ -24,24 +24,55 @@ import { Filter } from '@kbn/es-query'; // @ts-ignore import { uiModules } from 'ui/modules'; +import { npSetup } from 'ui/new_platform'; import { IndexPatterns } from 'src/legacy/core_plugins/data/public'; import { FilterBar, ApplyFiltersPopover } from '../filter'; import template from './apply_filter_directive.html'; // @ts-ignore import { mapAndFlattenFilters } from '../filter/filter_manager/lib/map_and_flatten_filters'; -// @ts-ignore /** @internal */ export const initLegacyModule = once((): void => { uiModules - .get('app/kibana') - .directive('filterBar', (reactDirective: any) => { - return reactDirective(wrapInI18nContext(FilterBar)); + .get('app/kibana', ['react']) + .directive('filterBar', () => { + return { + restrict: 'E', + template: '', + compile: (elem: any) => { + const child = document.createElement('filter-bar-helper'); + + // Copy attributes to the child directive + for (const attr of elem[0].attributes) { + child.setAttribute(attr.name, attr.value); + } + + child.setAttribute('ui-settings', 'uiSettings'); + + // Append helper directive + elem.append(child); + + const linkFn = ($scope: any) => { + $scope.uiSettings = npSetup.core.uiSettings; + }; + + return linkFn; + }, + }; }) - .directive('applyFiltersPopoverComponent', (reactDirective: any) => { - return reactDirective(wrapInI18nContext(ApplyFiltersPopover)); + .directive('filterBarHelper', (reactDirective: any) => { + return reactDirective(wrapInI18nContext(FilterBar), [ + ['uiSettings', { watchDepth: 'reference' }], + ['onFiltersUpdated', { watchDepth: 'reference' }], + ['indexPatterns', { watchDepth: 'collection' }], + ['filters', { watchDepth: 'collection' }], + ['className', { watchDepth: 'reference' }], + ]); }) + .directive('applyFiltersPopoverComponent', (reactDirective: any) => + reactDirective(wrapInI18nContext(ApplyFiltersPopover)) + ) .directive('applyFiltersPopover', (indexPatterns: IndexPatterns) => { return { template, @@ -57,13 +88,12 @@ export const initLegacyModule = once((): void => { // Each time the new filters change we want to rebuild (not just re-render) the "apply filters" // popover, because it has to reset its state whenever the new filters change. Setting a `key` // property on the component accomplishes this due to how React handles the `key` property. - $scope.$watch('filters', (filters: any) => { - mapAndFlattenFilters(indexPatterns, filters).then((mappedFilters: Filter[]) => { - $scope.state = { - filters: mappedFilters, - key: Date.now(), - }; - }); + $scope.$watch('filters', async (filters: any) => { + const mappedFilters: Filter[] = await mapAndFlattenFilters(indexPatterns, filters); + $scope.state = { + filters: mappedFilters, + key: Date.now(), + }; }); }, }; diff --git a/src/legacy/core_plugins/embeddable_api/public/ui_capabilities.test.mocks.ts b/src/legacy/core_plugins/embeddable_api/public/ui_capabilities.test.mocks.ts index b7e4a12fac247..69163522c2d99 100644 --- a/src/legacy/core_plugins/embeddable_api/public/ui_capabilities.test.mocks.ts +++ b/src/legacy/core_plugins/embeddable_api/public/ui_capabilities.test.mocks.ts @@ -24,3 +24,5 @@ jest.doMock('ui/capabilities', () => ({ }, }, })); + +jest.mock('ui/new_platform'); diff --git a/src/legacy/core_plugins/inspector_views/public/data/__snapshots__/data_view.test.js.snap b/src/legacy/core_plugins/inspector_views/public/data/__snapshots__/data_view.test.js.snap index dc1f6c17c93e4..11180877e04ab 100644 --- a/src/legacy/core_plugins/inspector_views/public/data/__snapshots__/data_view.test.js.snap +++ b/src/legacy/core_plugins/inspector_views/public/data/__snapshots__/data_view.test.js.snap @@ -119,93 +119,77 @@ exports[`Inspector Data View component should render empty state 1`] = ` } title="Test Data" > - +

+ +

+ + } + iconColor="subdued" + title={ +

+ +

+ } > - -
-
- -

- -

- - } - iconColor="subdued" - title={ -

- -

- } + +

+ + No data available + +

+
+
+ + +
- - + - -

- - No data available - -

-
- -
- - -
-

- - The element did not provide any data. - -

-
-
- - + The element did not provide any data. + +

- -
-
- - + + + +
+ `; @@ -328,91 +312,85 @@ exports[`Inspector Data View component should render loading state 1`] = ` } title="Test Data" > - - -
-
- + + + + + + + +
+ + +
- -
+ - - - - - - - - - -
- - -
-

- - Gathering data - -

-
-
-
- + Gathering data +
+

- +
- +
-
- - + +
+ `; diff --git a/src/legacy/core_plugins/inspector_views/public/data/data_view.js b/src/legacy/core_plugins/inspector_views/public/data/data_view.js index 32c340f110fc1..265e4dde29636 100644 --- a/src/legacy/core_plugins/inspector_views/public/data/data_view.js +++ b/src/legacy/core_plugins/inspector_views/public/data/data_view.js @@ -29,8 +29,6 @@ import { EuiText, } from '@elastic/eui'; -import { InspectorView } from 'ui/inspector'; - import { DataTableFormat, } from './data_table'; @@ -102,54 +100,51 @@ class DataViewComponent extends Component { renderNoData() { return ( - - + + + + } + body={ + +

- - } - body={ - -

- -

-
- } - /> -
+

+ + } + /> ); } renderLoading() { return ( - - - - - - - -

- -

-
-
-
-
-
+ + + + + + +

+ +

+
+
+
+
); } @@ -161,13 +156,11 @@ class DataViewComponent extends Component { } return ( - - - + ); } } diff --git a/src/legacy/core_plugins/inspector_views/public/data/data_view.test.js b/src/legacy/core_plugins/inspector_views/public/data/data_view.test.js index 29d536d60e83e..c616a7504d3e9 100644 --- a/src/legacy/core_plugins/inspector_views/public/data/data_view.test.js +++ b/src/legacy/core_plugins/inspector_views/public/data/data_view.test.js @@ -22,6 +22,7 @@ import { DataView } from './data_view'; import { DataAdapter } from 'ui/inspector/adapters'; import { mountWithIntl } from 'test_utils/enzyme_helpers'; +jest.mock('ui/new_platform'); jest.mock('./lib/export_csv', () => ({ exportAsCsv: jest.fn(), })); diff --git a/src/legacy/core_plugins/inspector_views/public/requests/requests_view.js b/src/legacy/core_plugins/inspector_views/public/requests/requests_view.js index 2ce47f62ef956..a949b2ebe6ea1 100644 --- a/src/legacy/core_plugins/inspector_views/public/requests/requests_view.js +++ b/src/legacy/core_plugins/inspector_views/public/requests/requests_view.js @@ -26,7 +26,6 @@ import { EuiTextColor, } from '@elastic/eui'; -import { InspectorView } from 'ui/inspector'; import { RequestStatus } from 'ui/inspector/adapters'; import { RequestSelector } from './request_selector'; @@ -68,36 +67,34 @@ class RequestsViewComponent extends Component { renderEmptyRequests() { return ( - - + + + + } + body={ + +

- - } - body={ - -

- -

-

- -

-
- } - /> -
+

+

+ +

+ + } + /> ); } @@ -111,7 +108,7 @@ class RequestsViewComponent extends Component { ).length; return ( - + <>

} - + ); } } diff --git a/src/legacy/core_plugins/kibana/public/dashboard/migrations/migrate_to_730_panels.test.ts b/src/legacy/core_plugins/kibana/public/dashboard/migrations/migrate_to_730_panels.test.ts index f18a1b29f7181..2c9c6ca65a92e 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/migrations/migrate_to_730_panels.test.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/migrations/migrate_to_730_panels.test.ts @@ -34,6 +34,8 @@ jest.mock( { virtual: true } ); +jest.mock('ui/new_platform'); + import { migratePanelsTo730 } from './migrate_to_730_panels'; import { SavedDashboardPanelTo60, SavedDashboardPanel730ToLatest } from '../types'; import { diff --git a/src/legacy/core_plugins/kibana/public/dashboard/np_core.test.mocks.ts b/src/legacy/core_plugins/kibana/public/dashboard/np_core.test.mocks.ts index fff5aeab599ea..e1d9cfac95268 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/np_core.test.mocks.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/np_core.test.mocks.ts @@ -17,35 +17,11 @@ * under the License. */ -import { fatalErrorsServiceMock, notificationServiceMock } from '../../../../../core/public/mocks'; - let modalContents: React.Component; export const getModalContents = () => modalContents; -jest.doMock('ui/new_platform', () => { - return { - npStart: { - core: { - overlays: { - openFlyout: jest.fn(), - openModal: (component: React.Component) => { - modalContents = component; - return { - close: jest.fn(), - }; - }, - }, - }, - }, - npSetup: { - core: { - fatalErrors: fatalErrorsServiceMock.createSetupContract(), - notifications: notificationServiceMock.createSetupContract(), - }, - }, - }; -}); +jest.mock('ui/new_platform'); jest.doMock('ui/metadata', () => ({ metadata: { diff --git a/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js b/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js index 4711fb1edb018..377fd72e9c771 100644 --- a/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js +++ b/src/legacy/core_plugins/kibana/public/discover/controllers/discover.js @@ -554,8 +554,8 @@ function discoverController( $scope.$watchCollection('state.sort', function (sort) { if (!sort) return; - // get the current sort from {key: val} to ["key", "val"]; - const currentSort = _.pairs($scope.searchSource.getField('sort')).pop(); + // get the current sort from searchSource as array of arrays + const currentSort = getSort.array($scope.searchSource.getField('sort'), $scope.indexPattern); // if the searchSource doesn't know, tell it so if (!angular.equals(sort, currentSort)) $scope.fetch(); @@ -862,8 +862,8 @@ function discoverController( .setField('filter', queryFilter.getFilters()); }); - $scope.setSortOrder = function setSortOrder(columnName, direction) { - $scope.state.sort = [columnName, direction]; + $scope.setSortOrder = function setSortOrder(sortPair) { + $scope.state.sort = sortPair; }; // TODO: On array fields, negating does not negate the combination, rather all terms diff --git a/src/legacy/core_plugins/kibana/public/discover/doc_table/__tests__/lib/get_sort.js b/src/legacy/core_plugins/kibana/public/discover/doc_table/__tests__/lib/get_sort.js index bec26d38d0fa7..b8b962b9f92d7 100644 --- a/src/legacy/core_plugins/kibana/public/discover/doc_table/__tests__/lib/get_sort.js +++ b/src/legacy/core_plugins/kibana/public/discover/doc_table/__tests__/lib/get_sort.js @@ -23,7 +23,7 @@ import ngMock from 'ng_mock'; import { getSort } from '../../lib/get_sort'; import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern'; -const defaultSort = { time: 'desc' }; +const defaultSort = [{ time: 'desc' }]; let indexPattern; describe('docTable', function () { @@ -38,11 +38,11 @@ describe('docTable', function () { expect(getSort).to.be.a(Function); }); - it('should return an object if passed a 2 item array', function () { - expect(getSort(['bytes', 'desc'], indexPattern)).to.eql({ bytes: 'desc' }); + it('should return an array of objects if passed a 2 item array', function () { + expect(getSort(['bytes', 'desc'], indexPattern)).to.eql([{ bytes: 'desc' }]); delete indexPattern.timeFieldName; - expect(getSort(['bytes', 'desc'], indexPattern)).to.eql({ bytes: 'desc' }); + expect(getSort(['bytes', 'desc'], indexPattern)).to.eql([{ bytes: 'desc' }]); }); it('should sort by the default when passed an unsortable field', function () { @@ -50,7 +50,7 @@ describe('docTable', function () { expect(getSort(['lol_nope', 'asc'], indexPattern)).to.eql(defaultSort); delete indexPattern.timeFieldName; - expect(getSort(['non-sortable', 'asc'], indexPattern)).to.eql({ _score: 'desc' }); + expect(getSort(['non-sortable', 'asc'], indexPattern)).to.eql([{ _score: 'desc' }]); }); it('should sort in reverse chrono order otherwise on time based patterns', function () { @@ -62,9 +62,9 @@ describe('docTable', function () { it('should sort by score on non-time patterns', function () { delete indexPattern.timeFieldName; - expect(getSort([], indexPattern)).to.eql({ _score: 'desc' }); - expect(getSort(['foo'], indexPattern)).to.eql({ _score: 'desc' }); - expect(getSort({ foo: 'bar' }, indexPattern)).to.eql({ _score: 'desc' }); + expect(getSort([], indexPattern)).to.eql([{ _score: 'desc' }]); + expect(getSort(['foo'], indexPattern)).to.eql([{ _score: 'desc' }]); + expect(getSort({ foo: 'bar' }, indexPattern)).to.eql([{ _score: 'desc' }]); }); }); @@ -73,8 +73,8 @@ describe('docTable', function () { expect(getSort.array).to.be.a(Function); }); - it('should return an array for sortable fields', function () { - expect(getSort.array(['bytes', 'desc'], indexPattern)).to.eql([ 'bytes', 'desc' ]); + it('should return an array of arrays for sortable fields', function () { + expect(getSort.array(['bytes', 'desc'], indexPattern)).to.eql([[ 'bytes', 'desc' ]]); }); }); }); diff --git a/src/legacy/core_plugins/kibana/public/discover/doc_table/components/table_header/__snapshots__/table_header.test.tsx.snap b/src/legacy/core_plugins/kibana/public/discover/doc_table/components/table_header/__snapshots__/table_header.test.tsx.snap index 689ec97154a15..3860a03d52716 100644 --- a/src/legacy/core_plugins/kibana/public/discover/doc_table/components/table_header/__snapshots__/table_header.test.tsx.snap +++ b/src/legacy/core_plugins/kibana/public/discover/doc_table/components/table_header/__snapshots__/table_header.test.tsx.snap @@ -19,7 +19,6 @@ exports[`TableHeader with time column renders correctly 1`] = ` class="euiToolTipAnchor" >