mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[Search][Sessions] Rename Background Sessions to Search Sessions (#87500)
* Rename Background Sessions to Search Sessions (with a send to background action) * doc * doc * jest fun * rename rfc * translations * update so name in features Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
130a8e766e
commit
91aed6f961
54 changed files with 417 additions and 472 deletions
|
@ -18,6 +18,6 @@ export interface ISearchSetup
|
|||
| --- | --- | --- |
|
||||
| [aggs](./kibana-plugin-plugins-data-public.isearchsetup.aggs.md) | <code>AggsSetup</code> | |
|
||||
| [session](./kibana-plugin-plugins-data-public.isearchsetup.session.md) | <code>ISessionService</code> | Current session management [ISessionService](./kibana-plugin-plugins-data-public.isessionservice.md) |
|
||||
| [sessionsClient](./kibana-plugin-plugins-data-public.isearchsetup.sessionsclient.md) | <code>ISessionsClient</code> | Background search sessions SO CRUD [ISessionsClient](./kibana-plugin-plugins-data-public.isessionsclient.md) |
|
||||
| [sessionsClient](./kibana-plugin-plugins-data-public.isearchsetup.sessionsclient.md) | <code>ISessionsClient</code> | Search sessions SO CRUD [ISessionsClient](./kibana-plugin-plugins-data-public.isessionsclient.md) |
|
||||
| [usageCollector](./kibana-plugin-plugins-data-public.isearchsetup.usagecollector.md) | <code>SearchUsageCollector</code> | |
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
## ISearchSetup.sessionsClient property
|
||||
|
||||
Background search sessions SO CRUD [ISessionsClient](./kibana-plugin-plugins-data-public.isessionsclient.md)
|
||||
Search sessions SO CRUD [ISessionsClient](./kibana-plugin-plugins-data-public.isessionsclient.md)
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
|
|
|
@ -20,6 +20,6 @@ export interface ISearchStart
|
|||
| [search](./kibana-plugin-plugins-data-public.isearchstart.search.md) | <code>ISearchGeneric</code> | low level search [ISearchGeneric](./kibana-plugin-plugins-data-public.isearchgeneric.md) |
|
||||
| [searchSource](./kibana-plugin-plugins-data-public.isearchstart.searchsource.md) | <code>ISearchStartSearchSource</code> | high level search [ISearchStartSearchSource](./kibana-plugin-plugins-data-public.isearchstartsearchsource.md) |
|
||||
| [session](./kibana-plugin-plugins-data-public.isearchstart.session.md) | <code>ISessionService</code> | Current session management [ISessionService](./kibana-plugin-plugins-data-public.isessionservice.md) |
|
||||
| [sessionsClient](./kibana-plugin-plugins-data-public.isearchstart.sessionsclient.md) | <code>ISessionsClient</code> | Background search sessions SO CRUD [ISessionsClient](./kibana-plugin-plugins-data-public.isessionsclient.md) |
|
||||
| [sessionsClient](./kibana-plugin-plugins-data-public.isearchstart.sessionsclient.md) | <code>ISessionsClient</code> | Search sessions SO CRUD [ISessionsClient](./kibana-plugin-plugins-data-public.isessionsclient.md) |
|
||||
| [showError](./kibana-plugin-plugins-data-public.isearchstart.showerror.md) | <code>(e: Error) => void</code> | |
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
## ISearchStart.sessionsClient property
|
||||
|
||||
Background search sessions SO CRUD [ISessionsClient](./kibana-plugin-plugins-data-public.isessionsclient.md)
|
||||
Search sessions SO CRUD [ISessionsClient](./kibana-plugin-plugins-data-public.isessionsclient.md)
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
| [KBN\_FIELD\_TYPES](./kibana-plugin-plugins-data-public.kbn_field_types.md) | \* |
|
||||
| [METRIC\_TYPES](./kibana-plugin-plugins-data-public.metric_types.md) | |
|
||||
| [QuerySuggestionTypes](./kibana-plugin-plugins-data-public.querysuggestiontypes.md) | |
|
||||
| [SessionState](./kibana-plugin-plugins-data-public.sessionstate.md) | Possible state that current session can be in |
|
||||
| [SearchSessionState](./kibana-plugin-plugins-data-public.searchsessionstate.md) | Possible state that current session can be in |
|
||||
| [SortDirection](./kibana-plugin-plugins-data-public.sortdirection.md) | |
|
||||
| [TimeoutErrorMode](./kibana-plugin-plugins-data-public.timeouterrormode.md) | |
|
||||
|
||||
|
@ -90,7 +90,7 @@
|
|||
| [SavedQueryService](./kibana-plugin-plugins-data-public.savedqueryservice.md) | |
|
||||
| [SearchError](./kibana-plugin-plugins-data-public.searcherror.md) | |
|
||||
| [SearchInterceptorDeps](./kibana-plugin-plugins-data-public.searchinterceptordeps.md) | |
|
||||
| [SearchSessionInfoProvider](./kibana-plugin-plugins-data-public.searchsessioninfoprovider.md) | Provide info about current search session to be stored in backgroundSearch saved object |
|
||||
| [SearchSessionInfoProvider](./kibana-plugin-plugins-data-public.searchsessioninfoprovider.md) | Provide info about current search session to be stored in the Search Session saved object |
|
||||
| [SearchSourceFields](./kibana-plugin-plugins-data-public.searchsourcefields.md) | search source fields |
|
||||
| [TabbedAggColumn](./kibana-plugin-plugins-data-public.tabbedaggcolumn.md) | \* |
|
||||
| [TabbedTable](./kibana-plugin-plugins-data-public.tabbedtable.md) | \* |
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
## SearchSessionInfoProvider.getName property
|
||||
|
||||
User-facing name of the session. e.g. will be displayed in background sessions management list
|
||||
User-facing name of the session. e.g. will be displayed in saved Search Sessions management list
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
## SearchSessionInfoProvider interface
|
||||
|
||||
Provide info about current search session to be stored in backgroundSearch saved object
|
||||
Provide info about current search session to be stored in the Search Session saved object
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
|
@ -16,6 +16,6 @@ export interface SearchSessionInfoProvider<ID extends UrlGeneratorId = UrlGenera
|
|||
|
||||
| Property | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| [getName](./kibana-plugin-plugins-data-public.searchsessioninfoprovider.getname.md) | <code>() => Promise<string></code> | User-facing name of the session. e.g. will be displayed in background sessions management list |
|
||||
| [getName](./kibana-plugin-plugins-data-public.searchsessioninfoprovider.getname.md) | <code>() => Promise<string></code> | User-facing name of the session. e.g. will be displayed in saved Search Sessions management list |
|
||||
| [getUrlGeneratorData](./kibana-plugin-plugins-data-public.searchsessioninfoprovider.geturlgeneratordata.md) | <code>() => Promise<{</code><br/><code> urlGeneratorId: ID;</code><br/><code> initialState: UrlGeneratorStateMapping[ID]['State'];</code><br/><code> restoreState: UrlGeneratorStateMapping[ID]['State'];</code><br/><code> }></code> | |
|
||||
|
||||
|
|
|
@ -1,25 +1,25 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SessionState](./kibana-plugin-plugins-data-public.sessionstate.md)
|
||||
[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchSessionState](./kibana-plugin-plugins-data-public.searchsessionstate.md)
|
||||
|
||||
## SessionState enum
|
||||
## SearchSessionState enum
|
||||
|
||||
Possible state that current session can be in
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
export declare enum SessionState
|
||||
export declare enum SearchSessionState
|
||||
```
|
||||
|
||||
## Enumeration Members
|
||||
|
||||
| Member | Value | Description |
|
||||
| --- | --- | --- |
|
||||
| BackgroundCompleted | <code>"backgroundCompleted"</code> | Page load completed with background session created. |
|
||||
| BackgroundLoading | <code>"backgroundLoading"</code> | Search request was sent to the background. The page is loading in background. |
|
||||
| BackgroundCompleted | <code>"backgroundCompleted"</code> | Page load completed with search session created. |
|
||||
| BackgroundLoading | <code>"backgroundLoading"</code> | Search session was sent to the background. The page is loading in background. |
|
||||
| Canceled | <code>"canceled"</code> | Current session requests where explicitly canceled by user Displaying none or partial results |
|
||||
| Completed | <code>"completed"</code> | No action was taken and the page completed loading without background session creation. |
|
||||
| Completed | <code>"completed"</code> | No action was taken and the page completed loading without search session creation. |
|
||||
| Loading | <code>"loading"</code> | Pending search request has not been sent to the background yet |
|
||||
| None | <code>"none"</code> | Session is not active, e.g. didn't start |
|
||||
| Restored | <code>"restored"</code> | Revisiting the page after background completion |
|
|
@ -295,7 +295,7 @@ export const SearchExamplesApp = ({
|
|||
<EuiFormLabel>Index Pattern</EuiFormLabel>
|
||||
<IndexPatternSelect
|
||||
placeholder={i18n.translate(
|
||||
'backgroundSessionExample.selectIndexPatternPlaceholder',
|
||||
'searchSessionExample.selectIndexPatternPlaceholder',
|
||||
{
|
||||
defaultMessage: 'Select index pattern',
|
||||
}
|
||||
|
|
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 108 KiB |
Before Width: | Height: | Size: 113 KiB After Width: | Height: | Size: 113 KiB |
|
@ -5,19 +5,19 @@
|
|||
- Architecture diagram: https://app.lucidchart.com/documents/edit/cf35b512-616a-4734-bc72-43dde70dbd44/0_0
|
||||
- Mockups: https://www.figma.com/proto/FD2M7MUpLScJKOyYjfbmev/ES-%2F-Query-Management-v4?node-id=440%3A1&viewport=984%2C-99%2C0.09413627535104752&scaling=scale-down
|
||||
- Old issue: https://github.com/elastic/kibana/issues/53335
|
||||
- Background search roadmap: https://github.com/elastic/kibana/issues/61738
|
||||
- Search Sessions roadmap: https://github.com/elastic/kibana/issues/61738
|
||||
- POC: https://github.com/elastic/kibana/pull/64641
|
||||
|
||||
# Summary
|
||||
|
||||
Background Sessions will enable Kibana applications and solutions to start a group of related search requests (such as those coming from a single load of a dashboard or SIEM timeline), navigate away or close the browser, then retrieve the results when they have completed.
|
||||
Search Sessions will enable Kibana applications and solutions to start a group of related search requests (such as those coming from a single load of a dashboard or SIEM timeline), navigate away or close the browser, then retrieve the results when they have completed.
|
||||
|
||||
# Basic example
|
||||
|
||||
At its core, background sessions are enabled via several new APIs, that:
|
||||
At its core, search sessions are enabled via several new APIs, that:
|
||||
- Start a session, associating multiple search requests with a single entity
|
||||
- Store the session (and continue search requests in the background)
|
||||
- Restore the background session
|
||||
- Restore the saved search session
|
||||
|
||||
```ts
|
||||
const searchService = dataPluginStart.search;
|
||||
|
@ -26,7 +26,7 @@ if (appState.sessionId) {
|
|||
// If we are restoring a session, set the session ID in the search service
|
||||
searchService.session.restore(sessionId);
|
||||
} else {
|
||||
// Otherwise, start a new background session to associate our search requests
|
||||
// Otherwise, start a new search session to associate our search requests
|
||||
appState.sessionId = searchService.session.start();
|
||||
}
|
||||
|
||||
|
@ -41,7 +41,7 @@ const response$ = await searchService.search(request);
|
|||
// Calling `session.store()`, creates a saved object for this session, allowing the user to navigate away.
|
||||
// The session object will be saved with all async search IDs that were executed so far.
|
||||
// Any follow up searches executed with this sessionId will be saved into this object as well.
|
||||
const backgroundSession = await searchService.session.store();
|
||||
const searchSession = await searchService.session.store();
|
||||
```
|
||||
|
||||
# Motivation
|
||||
|
@ -73,20 +73,20 @@ We call this entity a `session`, and when a user decides that they want to conti
|
|||
|
||||
This diagram matches any case where `data.search` is called from the front end:
|
||||
|
||||

|
||||

|
||||
|
||||
### Server side search
|
||||
|
||||
This case happens if the server is the one to invoke the `data.search` endpoint, for example with TSVB.
|
||||
|
||||

|
||||

|
||||
|
||||
## Data and Saved Objects
|
||||
|
||||
### Background Session Status
|
||||
### Search Session Status
|
||||
|
||||
```ts
|
||||
export enum BackgroundSessionStatus {
|
||||
export enum SearchSessionStatus {
|
||||
Running, // The session has at least one running search ID associated with it.
|
||||
Done, // All search IDs associated with this session have completed.
|
||||
Error, // At least one search ID associated with this session had an error.
|
||||
|
@ -96,27 +96,27 @@ export enum BackgroundSessionStatus {
|
|||
|
||||
### Saved Object Structure
|
||||
|
||||
The saved object created for a background session will be scoped to a single space, and will be a `hidden` saved object
|
||||
The saved object created for a search session will be scoped to a single space, and will be a `hidden` saved object
|
||||
(so that it doesn't show in the management listings). We will provide a separate interface for users to manage their own
|
||||
background sessions (which will use the `list`, `expire`, and `extend` methods described below, which will be restricted
|
||||
saved search sessions (which will use the `list`, `expire`, and `extend` methods described below, which will be restricted
|
||||
per-user).
|
||||
|
||||
```ts
|
||||
interface BackgroundSessionAttributes extends SavedObjectAttributes {
|
||||
interface SearchSessionAttributes extends SavedObjectAttributes {
|
||||
sessionId: string;
|
||||
userId: string; // Something unique to the user who generated this session, like username/realm-name/realm-type
|
||||
status: BackgroundSessionStatus;
|
||||
status: SearchSessionStatus;
|
||||
name: string;
|
||||
creation: Date;
|
||||
expiration: Date;
|
||||
idMapping: { [key: string]: string };
|
||||
url: string; // A URL relative to the Kibana root to retrieve the results of a completed background session (and/or to return to an incomplete view)
|
||||
metadata: { [key: string]: any } // Any data the specific application requires to restore a background session view
|
||||
url: string; // A URL relative to the Kibana root to retrieve the results of a completed search session (and/or to return to an incomplete view)
|
||||
metadata: { [key: string]: any } // Any data the specific application requires to restore a search session view
|
||||
}
|
||||
```
|
||||
|
||||
The URL that is provided will need to be generated by the specific application implementing background sessions. We
|
||||
recommend using the URL generator to ensure that URLs are backwards-compatible since background sessions may exist as
|
||||
The URL that is provided will need to be generated by the specific application implementing search sessions. We
|
||||
recommend using the URL generator to ensure that URLs are backwards-compatible since search sessions may exist as
|
||||
long as a user continues to extend the expiration.
|
||||
|
||||
## Frontend Services
|
||||
|
@ -153,10 +153,10 @@ interface ISessionService {
|
|||
* @param sessionId Session ID to store. Probably retrieved from `sessionService.get()`.
|
||||
* @param name A display name for the session.
|
||||
* @param url TODO: is the URL provided here? How?
|
||||
* @returns The stored `BackgroundSessionAttributes` object
|
||||
* @returns The stored `SearchSessionAttributes` object
|
||||
* @throws Throws an error in OSS.
|
||||
*/
|
||||
store: (sessionId: string, name: string, url: string) => Promise<BackgroundSessionAttributes>
|
||||
store: (sessionId: string, name: string, url: string) => Promise<SearchSessionAttributes>
|
||||
|
||||
/**
|
||||
* @returns Is the current session stored (i.e. is there a saved object corresponding with this sessionId).
|
||||
|
@ -188,17 +188,17 @@ interface ISessionService {
|
|||
|
||||
/**
|
||||
* @param sessionId the ID of the session to retrieve the saved object.
|
||||
* @returns a filtered list of BackgroundSessionAttributes objects.
|
||||
* @returns a filtered list of SearchSessionAttributes objects.
|
||||
* @throws Throws an error in OSS.
|
||||
*/
|
||||
get: (sessionId: string) => Promise<BackgroundSessionAttributes>
|
||||
get: (sessionId: string) => Promise<SearchSessionAttributes>
|
||||
|
||||
/**
|
||||
* @param options The options to query for specific background session saved objects.
|
||||
* @returns a filtered list of BackgroundSessionAttributes objects.
|
||||
* @param options The options to query for specific search session saved objects.
|
||||
* @returns a filtered list of SearchSessionAttributes objects.
|
||||
* @throws Throws an error in OSS.
|
||||
*/
|
||||
list: (options: SavedObjectsFindOptions) => Promise<BackgroundSessionAttributes[]>
|
||||
list: (options: SavedObjectsFindOptions) => Promise<SearchSessionAttributes[]>
|
||||
|
||||
/**
|
||||
* Clears out any session info as well as the current session. Called internally whenever the user navigates
|
||||
|
@ -241,12 +241,12 @@ attempt to find the correct id within the saved object, and use it to retrieve t
|
|||
```ts
|
||||
interface ISessionService {
|
||||
/**
|
||||
* Adds a search ID to a Background Session, if it exists.
|
||||
* Adds a search ID to a Search Session, if it exists.
|
||||
* Also extends the expiration of the search ID to match the session's expiration.
|
||||
* @param request
|
||||
* @param sessionId
|
||||
* @param searchId
|
||||
* @returns true if id was added, false if Background Session doesn't exist or if there was an error while updating.
|
||||
* @returns true if id was added, false if Search Session doesn't exist or if there was an error while updating.
|
||||
* @throws an error if `searchId` already exists in the mapping for this `sessionId`
|
||||
*/
|
||||
trackSearchId: (
|
||||
|
@ -256,21 +256,21 @@ interface ISessionService {
|
|||
) => Promise<boolean>
|
||||
|
||||
/**
|
||||
* Get a Background Session object.
|
||||
* Get a Search Session object.
|
||||
* @param request
|
||||
* @param sessionId
|
||||
* @returns the Background Session object if exists, or undefined.
|
||||
* @returns the Search Session object if exists, or undefined.
|
||||
*/
|
||||
get: async (
|
||||
request: KibanaRequest,
|
||||
sessionId: string
|
||||
) => Promise<BackgroundSessionAttributes?>
|
||||
) => Promise<SearchSessionAttributes?>
|
||||
|
||||
/**
|
||||
* Get a searchId from a Background Session object.
|
||||
* Get a searchId from a Search Session object.
|
||||
* @param request
|
||||
* @param sessionId
|
||||
* @returns the searchID if exists on the Background Session, or undefined.
|
||||
* @returns the searchID if exists on the Search Session, or undefined.
|
||||
*/
|
||||
getSearchId: async (
|
||||
request: KibanaRequest,
|
||||
|
@ -283,7 +283,7 @@ interface ISessionService {
|
|||
* @param sessionId Session ID to store. Probably retrieved from `sessionService.get()`.
|
||||
* @param searchIdMap A mapping of hashed requests mapped to the corresponding searchId.
|
||||
* @param url TODO: is the URL provided here? How?
|
||||
* @returns The stored `BackgroundSessionAttributes` object
|
||||
* @returns The stored `SearchSessionAttributes` object
|
||||
* @throws Throws an error in OSS.
|
||||
* @internal (Consumers should use searchInterceptor.sendToBackground())
|
||||
*/
|
||||
|
@ -293,7 +293,7 @@ interface ISessionService {
|
|||
name: string,
|
||||
url: string,
|
||||
searchIdMapping?: Record<string, string>
|
||||
) => Promise<BackgroundSessionAttributes>
|
||||
) => Promise<SearchSessionAttributes>
|
||||
|
||||
/**
|
||||
* Mark a session as and all associated searchIds as expired.
|
||||
|
@ -322,7 +322,7 @@ interface ISessionService {
|
|||
) => Promise<boolean>
|
||||
|
||||
/**
|
||||
* Get a list of background session objects.
|
||||
* Get a list of Search Session objects.
|
||||
* @param request
|
||||
* @param sessionId
|
||||
* @returns success status
|
||||
|
@ -330,7 +330,7 @@ interface ISessionService {
|
|||
*/
|
||||
list: async (
|
||||
request: KibanaRequest,
|
||||
) => Promise<BackgroundSessionAttributes[]>
|
||||
) => Promise<SearchSessionAttributes[]>
|
||||
|
||||
/**
|
||||
* Update the status of a given session
|
||||
|
@ -343,7 +343,7 @@ interface ISessionService {
|
|||
updateStatus: async (
|
||||
request: KibanaRequest,
|
||||
sessionId: string,
|
||||
status: BackgroundSessionStatus
|
||||
status: SearchSessionStatus
|
||||
) => Promise<boolean>
|
||||
}
|
||||
|
||||
|
@ -381,13 +381,13 @@ Each route exposes the corresponding method from the Session Service (used only
|
|||
|
||||
### Search Strategy Integration
|
||||
|
||||
If the `EnhancedEsSearchStrategy` receives a `restore` option, it will attempt reloading data using the Background Session saved object matching the provided `sessionId`. If there are any errors during that process, the strategy will return an error response and *not attempt to re-run the request.
|
||||
If the `EnhancedEsSearchStrategy` receives a `restore` option, it will attempt reloading data using the Search Session saved object matching the provided `sessionId`. If there are any errors during that process, the strategy will return an error response and *not attempt to re-run the request.
|
||||
|
||||
The strategy will track the asyncId on the server side, if `trackId` option is provided.
|
||||
|
||||
### Monitoring Service
|
||||
|
||||
The `data` plugin will register a task with the task manager, periodically monitoring the status of incomplete background sessions.
|
||||
The `data` plugin will register a task with the task manager, periodically monitoring the status of incomplete search sessions.
|
||||
|
||||
It will query the list of all incomplete sessions, and check the status of each search that is executing. If the search requests are all complete, it will update the corresponding saved object to have a `status` of `complete`. If any of the searches return an error, it will update the saved object to an `error` state. If the search requests have expired, it will update the saved object to an `expired` state. Expired sessions will be purged once they are older than the time definedby the `EXPIRED_SESSION_TTL` advanced setting.
|
||||
|
||||
|
@ -405,23 +405,23 @@ There are two potential scenarios:
|
|||
|
||||
Both scenarios require careful attention during the UI design and implementation.
|
||||
|
||||
The former can be resolved by clearly displaying the creation time of the restored Background Session. We could also attempt translating relative dates to absolute one's, but this might be challenging as relative dates may appear deeply nested within the DSL.
|
||||
The former can be resolved by clearly displaying the creation time of the restored Search Session. We could also attempt translating relative dates to absolute one's, but this might be challenging as relative dates may appear deeply nested within the DSL.
|
||||
|
||||
The latter case happens at the moment for the timepicker only: The relative date is being translated each time into an absolute one, before being sent to Elasticsearch. In order to avoid issues, we'll have to make sure that restore URLs are generated with an absolute date, to make sure they are restored correctly.
|
||||
|
||||
#### Changing a restored session
|
||||
|
||||
If you have restored a Background Session, making any type of change to it (time range, filters, etc.) will trigger new (potentially long) searches. There should be a clear indication in the UI that the data is no longer stored. A user then may choose to send it to background, resulting in a new Background Session being saved.
|
||||
If you have restored a Search Session, making any type of change to it (time range, filters, etc.) will trigger new (potentially long) searches. There should be a clear indication in the UI that the data is no longer stored. A user then may choose to send it to background, resulting in a new Search Session being saved.
|
||||
|
||||
#### Loading an errored \ expired \ canceled session
|
||||
|
||||
When trying to restore a Background Session, if any of the requests hashes don't match the ones saved, or if any of the saved async search IDs are expired, a meaningful error code will be returned by the server **by those requests**. It is each application's responsibility to handle these errors appropriately.
|
||||
When trying to restore a Search Session, if any of the requests hashes don't match the ones saved, or if any of the saved async search IDs are expired, a meaningful error code will be returned by the server **by those requests**. It is each application's responsibility to handle these errors appropriately.
|
||||
|
||||
In such a scenario, the session will be partially restored.
|
||||
|
||||
#### Extending Expiration
|
||||
|
||||
Sessions are given an expiration date defined in an advanced setting (5 days by default). This expiration date is measured from the time the Background Session is saved, and it includes the time it takes to generate the results.
|
||||
Sessions are given an expiration date defined in an advanced setting (5 days by default). This expiration date is measured from the time the Search Session is saved, and it includes the time it takes to generate the results.
|
||||
|
||||
A session's expiration date may be extended indefinitely. However, if a session was canceled or has already expired, it needs to be re-run.
|
||||
|
||||
|
@ -444,7 +444,7 @@ so we feel comfortable moving forward with this approach.
|
|||
|
||||
Two potential drawbacks stem from storing things in server memory. If a Kibana server is restarted, in-memory results
|
||||
will be lost. (This can be an issue if a search request has started, and the user has sent to background, but the
|
||||
background session saved object has not yet been updated with the search request ID.) In such cases, the user interface
|
||||
search session saved object has not yet been updated with the search request ID.) In such cases, the user interface
|
||||
will need to indicate errors for requests that were not stored in the saved object.
|
||||
|
||||
There is also the consideration of the memory footprint of the Kibana server; however, since
|
||||
|
@ -452,7 +452,7 @@ we are only storing a hash of the request and search request ID, and are periodi
|
|||
Services and Routes), we do not anticipate the footprint to increase significantly.
|
||||
|
||||
The results of search requests that have been sent to the background will be stored in Elasticsearch for several days,
|
||||
even if they will only be retrieved once. This will be mitigated by allowing the user manually delete a background
|
||||
even if they will only be retrieved once. This will be mitigated by allowing the user manually delete a search
|
||||
session object after it has been accessed.
|
||||
|
||||
# Alternatives
|
||||
|
@ -463,7 +463,7 @@ What other designs have been considered? What is the impact of not doing this?
|
|||
|
||||
(See "Basic example" above.)
|
||||
|
||||
Any application or solution that uses the `data` plugin `search` services will be able to facilitate background sessions
|
||||
Any application or solution that uses the `data` plugin `search` services will be able to facilitate search sessions
|
||||
fairly simply. The public side will need to create/clear sessions when appropriate, and ensure the `sessionId` is sent
|
||||
with all search requests. It will also need to ensure that any necessary application data, as well as a `restoreUrl` is
|
||||
sent when creating the saved object.
|
|
@ -385,7 +385,7 @@ export {
|
|||
SearchRequest,
|
||||
SearchSourceFields,
|
||||
SortDirection,
|
||||
SessionState,
|
||||
SearchSessionState,
|
||||
// expression functions and types
|
||||
EsdslExpressionFunctionDefinition,
|
||||
EsRawResponseExpressionTypeDefinition,
|
||||
|
|
|
@ -2311,6 +2311,17 @@ export interface SearchSessionInfoProvider<ID extends UrlGeneratorId = UrlGenera
|
|||
}>;
|
||||
}
|
||||
|
||||
// @public
|
||||
export enum SearchSessionState {
|
||||
BackgroundCompleted = "backgroundCompleted",
|
||||
BackgroundLoading = "backgroundLoading",
|
||||
Canceled = "canceled",
|
||||
Completed = "completed",
|
||||
Loading = "loading",
|
||||
None = "none",
|
||||
Restored = "restored"
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export class SearchSource {
|
||||
// Warning: (ae-forgotten-export) The symbol "SearchSourceDependencies" needs to be exported by the entry point index.d.ts
|
||||
|
@ -2418,17 +2429,6 @@ export class SearchTimeoutError extends KbnError {
|
|||
mode: TimeoutErrorMode;
|
||||
}
|
||||
|
||||
// @public
|
||||
export enum SessionState {
|
||||
BackgroundCompleted = "backgroundCompleted",
|
||||
BackgroundLoading = "backgroundLoading",
|
||||
Canceled = "canceled",
|
||||
Completed = "completed",
|
||||
Loading = "loading",
|
||||
None = "none",
|
||||
Restored = "restored"
|
||||
}
|
||||
|
||||
// Warning: (ae-missing-release-tag) "SortDirection" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
|
@ -2620,7 +2620,7 @@ export const UI_SETTINGS: {
|
|||
// src/plugins/data/public/index.ts:433:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:436:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/query/state_sync/connect_to_query_state.ts:45:5 - (ae-forgotten-export) The symbol "FilterStateStore" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/search/session/session_service.ts:46:5 - (ae-forgotten-export) The symbol "UrlGeneratorStateMapping" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/search/session/session_service.ts:50:5 - (ae-forgotten-export) The symbol "UrlGeneratorStateMapping" needs to be exported by the entry point index.d.ts
|
||||
|
||||
// (No @packageDocumentation comment for this package)
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ export {
|
|||
SessionService,
|
||||
ISessionService,
|
||||
SearchSessionInfoProvider,
|
||||
SessionState,
|
||||
SearchSessionState,
|
||||
SessionsClient,
|
||||
ISessionsClient,
|
||||
} from './session';
|
||||
|
|
|
@ -18,5 +18,5 @@
|
|||
*/
|
||||
|
||||
export { SessionService, ISessionService, SearchSessionInfoProvider } from './session_service';
|
||||
export { SessionState } from './session_state';
|
||||
export { SearchSessionState } from './search_session_state';
|
||||
export { SessionsClient, ISessionsClient } from './sessions_client';
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
import { BehaviorSubject, Subject } from 'rxjs';
|
||||
import { ISessionsClient } from './sessions_client';
|
||||
import { ISessionService } from './session_service';
|
||||
import { SessionState } from './session_state';
|
||||
import { SearchSessionState } from './search_session_state';
|
||||
|
||||
export function getSessionsClientMock(): jest.Mocked<ISessionsClient> {
|
||||
return {
|
||||
|
@ -39,7 +39,7 @@ export function getSessionServiceMock(): jest.Mocked<ISessionService> {
|
|||
restore: jest.fn(),
|
||||
getSessionId: jest.fn(),
|
||||
getSession$: jest.fn(() => new BehaviorSubject(undefined).asObservable()),
|
||||
state$: new BehaviorSubject<SessionState>(SessionState.None).asObservable(),
|
||||
state$: new BehaviorSubject<SearchSessionState>(SearchSessionState.None).asObservable(),
|
||||
setSearchSessionInfoProvider: jest.fn(),
|
||||
trackSearch: jest.fn((searchDescriptor) => () => {}),
|
||||
destroy: jest.fn(),
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { createSessionStateContainer, SessionState } from './session_state';
|
||||
import { createSessionStateContainer, SearchSessionState } from './search_session_state';
|
||||
|
||||
describe('Session state container', () => {
|
||||
const { stateContainer: state } = createSessionStateContainer();
|
||||
|
@ -29,7 +29,7 @@ describe('Session state container', () => {
|
|||
describe('transitions', () => {
|
||||
test('start', () => {
|
||||
state.transitions.start();
|
||||
expect(state.selectors.getState()).toBe(SessionState.None);
|
||||
expect(state.selectors.getState()).toBe(SearchSessionState.None);
|
||||
expect(state.get().sessionId).not.toBeUndefined();
|
||||
});
|
||||
|
||||
|
@ -39,22 +39,22 @@ describe('Session state container', () => {
|
|||
state.transitions.start();
|
||||
state.transitions.trackSearch({});
|
||||
|
||||
expect(state.selectors.getState()).toBe(SessionState.Loading);
|
||||
expect(state.selectors.getState()).toBe(SearchSessionState.Loading);
|
||||
});
|
||||
|
||||
test('untrack', () => {
|
||||
state.transitions.start();
|
||||
const search = {};
|
||||
state.transitions.trackSearch(search);
|
||||
expect(state.selectors.getState()).toBe(SessionState.Loading);
|
||||
expect(state.selectors.getState()).toBe(SearchSessionState.Loading);
|
||||
state.transitions.unTrackSearch(search);
|
||||
expect(state.selectors.getState()).toBe(SessionState.Completed);
|
||||
expect(state.selectors.getState()).toBe(SearchSessionState.Completed);
|
||||
});
|
||||
|
||||
test('clear', () => {
|
||||
state.transitions.start();
|
||||
state.transitions.clear();
|
||||
expect(state.selectors.getState()).toBe(SessionState.None);
|
||||
expect(state.selectors.getState()).toBe(SearchSessionState.None);
|
||||
expect(state.get().sessionId).toBeUndefined();
|
||||
});
|
||||
|
||||
|
@ -64,11 +64,11 @@ describe('Session state container', () => {
|
|||
state.transitions.start();
|
||||
const search = {};
|
||||
state.transitions.trackSearch(search);
|
||||
expect(state.selectors.getState()).toBe(SessionState.Loading);
|
||||
expect(state.selectors.getState()).toBe(SearchSessionState.Loading);
|
||||
state.transitions.cancel();
|
||||
expect(state.selectors.getState()).toBe(SessionState.Canceled);
|
||||
expect(state.selectors.getState()).toBe(SearchSessionState.Canceled);
|
||||
state.transitions.clear();
|
||||
expect(state.selectors.getState()).toBe(SessionState.None);
|
||||
expect(state.selectors.getState()).toBe(SearchSessionState.None);
|
||||
});
|
||||
|
||||
test('store -> completed', () => {
|
||||
|
@ -77,48 +77,48 @@ describe('Session state container', () => {
|
|||
state.transitions.start();
|
||||
const search = {};
|
||||
state.transitions.trackSearch(search);
|
||||
expect(state.selectors.getState()).toBe(SessionState.Loading);
|
||||
expect(state.selectors.getState()).toBe(SearchSessionState.Loading);
|
||||
state.transitions.store();
|
||||
expect(state.selectors.getState()).toBe(SessionState.BackgroundLoading);
|
||||
expect(state.selectors.getState()).toBe(SearchSessionState.BackgroundLoading);
|
||||
state.transitions.unTrackSearch(search);
|
||||
expect(state.selectors.getState()).toBe(SessionState.BackgroundCompleted);
|
||||
expect(state.selectors.getState()).toBe(SearchSessionState.BackgroundCompleted);
|
||||
state.transitions.clear();
|
||||
expect(state.selectors.getState()).toBe(SessionState.None);
|
||||
expect(state.selectors.getState()).toBe(SearchSessionState.None);
|
||||
});
|
||||
test('store -> cancel', () => {
|
||||
state.transitions.start();
|
||||
const search = {};
|
||||
state.transitions.trackSearch(search);
|
||||
expect(state.selectors.getState()).toBe(SessionState.Loading);
|
||||
expect(state.selectors.getState()).toBe(SearchSessionState.Loading);
|
||||
state.transitions.store();
|
||||
expect(state.selectors.getState()).toBe(SessionState.BackgroundLoading);
|
||||
expect(state.selectors.getState()).toBe(SearchSessionState.BackgroundLoading);
|
||||
state.transitions.cancel();
|
||||
expect(state.selectors.getState()).toBe(SessionState.Canceled);
|
||||
expect(state.selectors.getState()).toBe(SearchSessionState.Canceled);
|
||||
|
||||
state.transitions.trackSearch(search);
|
||||
expect(state.selectors.getState()).toBe(SessionState.Canceled);
|
||||
expect(state.selectors.getState()).toBe(SearchSessionState.Canceled);
|
||||
|
||||
state.transitions.start();
|
||||
expect(state.selectors.getState()).toBe(SessionState.None);
|
||||
expect(state.selectors.getState()).toBe(SearchSessionState.None);
|
||||
});
|
||||
|
||||
test('restore', () => {
|
||||
const id = 'id';
|
||||
state.transitions.restore(id);
|
||||
expect(state.selectors.getState()).toBe(SessionState.None);
|
||||
expect(state.selectors.getState()).toBe(SearchSessionState.None);
|
||||
const search = {};
|
||||
state.transitions.trackSearch(search);
|
||||
expect(state.selectors.getState()).toBe(SessionState.BackgroundLoading);
|
||||
expect(state.selectors.getState()).toBe(SearchSessionState.BackgroundLoading);
|
||||
state.transitions.unTrackSearch(search);
|
||||
|
||||
expect(state.selectors.getState()).toBe(SessionState.Restored);
|
||||
expect(state.selectors.getState()).toBe(SearchSessionState.Restored);
|
||||
expect(() => state.transitions.store()).toThrowError();
|
||||
expect(state.selectors.getState()).toBe(SessionState.Restored);
|
||||
expect(state.selectors.getState()).toBe(SearchSessionState.Restored);
|
||||
expect(() => state.transitions.cancel()).toThrowError();
|
||||
expect(state.selectors.getState()).toBe(SessionState.Restored);
|
||||
expect(state.selectors.getState()).toBe(SearchSessionState.Restored);
|
||||
|
||||
state.transitions.start();
|
||||
expect(state.selectors.getState()).toBe(SessionState.None);
|
||||
expect(state.selectors.getState()).toBe(SearchSessionState.None);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -27,7 +27,7 @@ import { createStateContainer, StateContainer } from '../../../../kibana_utils/p
|
|||
*
|
||||
* @public
|
||||
*/
|
||||
export enum SessionState {
|
||||
export enum SearchSessionState {
|
||||
/**
|
||||
* Session is not active, e.g. didn't start
|
||||
*/
|
||||
|
@ -39,18 +39,18 @@ export enum SessionState {
|
|||
Loading = 'loading',
|
||||
|
||||
/**
|
||||
* No action was taken and the page completed loading without background session creation.
|
||||
* No action was taken and the page completed loading without search session creation.
|
||||
*/
|
||||
Completed = 'completed',
|
||||
|
||||
/**
|
||||
* Search request was sent to the background.
|
||||
* Search session was sent to the background.
|
||||
* The page is loading in background.
|
||||
*/
|
||||
BackgroundLoading = 'backgroundLoading',
|
||||
|
||||
/**
|
||||
* Page load completed with background session created.
|
||||
* Page load completed with search session created.
|
||||
*/
|
||||
BackgroundCompleted = 'backgroundCompleted',
|
||||
|
||||
|
@ -68,7 +68,7 @@ export enum SessionState {
|
|||
|
||||
/**
|
||||
* Internal state of SessionService
|
||||
* {@link SessionState} is inferred from this state
|
||||
* {@link SearchSessionState} is inferred from this state
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
|
@ -179,27 +179,29 @@ export interface SessionPureSelectors<
|
|||
SearchDescriptor = unknown,
|
||||
S = SessionStateInternal<SearchDescriptor>
|
||||
> {
|
||||
getState: (state: S) => () => SessionState;
|
||||
getState: (state: S) => () => SearchSessionState;
|
||||
}
|
||||
|
||||
export const sessionPureSelectors: SessionPureSelectors = {
|
||||
getState: (state) => () => {
|
||||
if (!state.sessionId) return SessionState.None;
|
||||
if (!state.isStarted) return SessionState.None;
|
||||
if (state.isCanceled) return SessionState.Canceled;
|
||||
if (!state.sessionId) return SearchSessionState.None;
|
||||
if (!state.isStarted) return SearchSessionState.None;
|
||||
if (state.isCanceled) return SearchSessionState.Canceled;
|
||||
switch (true) {
|
||||
case state.isRestore:
|
||||
return state.pendingSearches.length > 0
|
||||
? SessionState.BackgroundLoading
|
||||
: SessionState.Restored;
|
||||
? SearchSessionState.BackgroundLoading
|
||||
: SearchSessionState.Restored;
|
||||
case state.isStored:
|
||||
return state.pendingSearches.length > 0
|
||||
? SessionState.BackgroundLoading
|
||||
: SessionState.BackgroundCompleted;
|
||||
? SearchSessionState.BackgroundLoading
|
||||
: SearchSessionState.BackgroundCompleted;
|
||||
default:
|
||||
return state.pendingSearches.length > 0 ? SessionState.Loading : SessionState.Completed;
|
||||
return state.pendingSearches.length > 0
|
||||
? SearchSessionState.Loading
|
||||
: SearchSessionState.Completed;
|
||||
}
|
||||
return SessionState.None;
|
||||
return SearchSessionState.None;
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -213,7 +215,7 @@ export const createSessionStateContainer = <SearchDescriptor = unknown>(
|
|||
{ freeze = true }: { freeze: boolean } = { freeze: true }
|
||||
): {
|
||||
stateContainer: SessionStateContainer<SearchDescriptor>;
|
||||
sessionState$: Observable<SessionState>;
|
||||
sessionState$: Observable<SearchSessionState>;
|
||||
} => {
|
||||
const stateContainer = createStateContainer(
|
||||
createSessionDefaultState(),
|
||||
|
@ -222,7 +224,7 @@ export const createSessionStateContainer = <SearchDescriptor = unknown>(
|
|||
freeze ? undefined : { freeze: (s) => s }
|
||||
) as SessionStateContainer<SearchDescriptor>;
|
||||
|
||||
const sessionState$: Observable<SessionState> = stateContainer.state$.pipe(
|
||||
const sessionState$: Observable<SearchSessionState> = stateContainer.state$.pipe(
|
||||
map(() => stateContainer.selectors.getState()),
|
||||
distinctUntilChanged(),
|
||||
shareReplay(1)
|
|
@ -22,11 +22,11 @@ import { coreMock } from '../../../../../core/public/mocks';
|
|||
import { take, toArray } from 'rxjs/operators';
|
||||
import { getSessionsClientMock } from './mocks';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { SessionState } from './session_state';
|
||||
import { SearchSessionState } from './search_session_state';
|
||||
|
||||
describe('Session service', () => {
|
||||
let sessionService: ISessionService;
|
||||
let state$: BehaviorSubject<SessionState>;
|
||||
let state$: BehaviorSubject<SearchSessionState>;
|
||||
|
||||
beforeEach(() => {
|
||||
const initializerContext = coreMock.createPluginInitializerContext();
|
||||
|
@ -36,7 +36,7 @@ describe('Session service', () => {
|
|||
getSessionsClientMock(),
|
||||
{ freezeState: false } // needed to use mocks inside state container
|
||||
);
|
||||
state$ = new BehaviorSubject<SessionState>(SessionState.None);
|
||||
state$ = new BehaviorSubject<SearchSessionState>(SearchSessionState.None);
|
||||
sessionService.state$.subscribe(state$);
|
||||
});
|
||||
|
||||
|
@ -65,17 +65,17 @@ describe('Session service', () => {
|
|||
|
||||
it('Tracks searches for current session', () => {
|
||||
expect(() => sessionService.trackSearch({ abort: () => {} })).toThrowError();
|
||||
expect(state$.getValue()).toBe(SessionState.None);
|
||||
expect(state$.getValue()).toBe(SearchSessionState.None);
|
||||
|
||||
sessionService.start();
|
||||
const untrack1 = sessionService.trackSearch({ abort: () => {} });
|
||||
expect(state$.getValue()).toBe(SessionState.Loading);
|
||||
expect(state$.getValue()).toBe(SearchSessionState.Loading);
|
||||
const untrack2 = sessionService.trackSearch({ abort: () => {} });
|
||||
expect(state$.getValue()).toBe(SessionState.Loading);
|
||||
expect(state$.getValue()).toBe(SearchSessionState.Loading);
|
||||
untrack1();
|
||||
expect(state$.getValue()).toBe(SessionState.Loading);
|
||||
expect(state$.getValue()).toBe(SearchSessionState.Loading);
|
||||
untrack2();
|
||||
expect(state$.getValue()).toBe(SessionState.Completed);
|
||||
expect(state$.getValue()).toBe(SearchSessionState.Completed);
|
||||
});
|
||||
|
||||
it('Cancels all tracked searches within current session', async () => {
|
||||
|
|
|
@ -23,7 +23,11 @@ import { Observable, Subject, Subscription } from 'rxjs';
|
|||
import { PluginInitializerContext, StartServicesAccessor } from 'kibana/public';
|
||||
import { UrlGeneratorId, UrlGeneratorStateMapping } from '../../../../share/public/';
|
||||
import { ConfigSchema } from '../../../config';
|
||||
import { createSessionStateContainer, SessionState, SessionStateContainer } from './session_state';
|
||||
import {
|
||||
createSessionStateContainer,
|
||||
SearchSessionState,
|
||||
SessionStateContainer,
|
||||
} from './search_session_state';
|
||||
import { ISessionsClient } from './sessions_client';
|
||||
|
||||
export type ISessionService = PublicContract<SessionService>;
|
||||
|
@ -33,12 +37,12 @@ export interface TrackSearchDescriptor {
|
|||
}
|
||||
|
||||
/**
|
||||
* Provide info about current search session to be stored in backgroundSearch saved object
|
||||
* Provide info about current search session to be stored in the Search Session saved object
|
||||
*/
|
||||
export interface SearchSessionInfoProvider<ID extends UrlGeneratorId = UrlGeneratorId> {
|
||||
/**
|
||||
* User-facing name of the session.
|
||||
* e.g. will be displayed in background sessions management list
|
||||
* e.g. will be displayed in saved Search Sessions management list
|
||||
*/
|
||||
getName: () => Promise<string>;
|
||||
getUrlGeneratorData: () => Promise<{
|
||||
|
@ -52,7 +56,7 @@ export interface SearchSessionInfoProvider<ID extends UrlGeneratorId = UrlGenera
|
|||
* Responsible for tracking a current search session. Supports only a single session at a time.
|
||||
*/
|
||||
export class SessionService {
|
||||
public readonly state$: Observable<SessionState>;
|
||||
public readonly state$: Observable<SearchSessionState>;
|
||||
private readonly state: SessionStateContainer<TrackSearchDescriptor>;
|
||||
|
||||
private searchSessionInfoProvider?: SearchSessionInfoProvider;
|
||||
|
@ -95,7 +99,7 @@ export class SessionService {
|
|||
|
||||
/**
|
||||
* Set a provider of info about current session
|
||||
* This will be used for creating a background session saved object
|
||||
* This will be used for creating a search session saved object
|
||||
* @param searchSessionInfoProvider
|
||||
*/
|
||||
public setSearchSessionInfoProvider<ID extends UrlGeneratorId = UrlGeneratorId>(
|
||||
|
@ -184,7 +188,7 @@ export class SessionService {
|
|||
private refresh$ = new Subject<void>();
|
||||
/**
|
||||
* Observable emits when search result refresh was requested
|
||||
* For example, search to background UI could have it's own "refresh" button
|
||||
* For example, the UI could have it's own "refresh" button
|
||||
* Application would use this observable to handle user interaction on that button
|
||||
*/
|
||||
public onRefresh$ = this.refresh$.asObservable();
|
||||
|
|
|
@ -27,7 +27,7 @@ export interface SessionsClientDeps {
|
|||
}
|
||||
|
||||
/**
|
||||
* CRUD backgroundSession SO
|
||||
* CRUD Search Session SO
|
||||
*/
|
||||
export class SessionsClient {
|
||||
private readonly http: HttpSetup;
|
||||
|
|
|
@ -45,7 +45,7 @@ export interface ISearchSetup {
|
|||
*/
|
||||
session: ISessionService;
|
||||
/**
|
||||
* Background search sessions SO CRUD
|
||||
* Search sessions SO CRUD
|
||||
* {@link ISessionsClient}
|
||||
*/
|
||||
sessionsClient: ISessionsClient;
|
||||
|
@ -84,7 +84,7 @@ export interface ISearchStart {
|
|||
*/
|
||||
session: ISessionService;
|
||||
/**
|
||||
* Background search sessions SO CRUD
|
||||
* Search sessions SO CRUD
|
||||
* {@link ISessionsClient}
|
||||
*/
|
||||
sessionsClient: ISessionsClient;
|
||||
|
|
|
@ -23,7 +23,7 @@ import { ISearchStrategy } from '../types';
|
|||
import { ISessionService } from './types';
|
||||
|
||||
/**
|
||||
* The OSS session service. See data_enhanced in X-Pack for the background session service.
|
||||
* The OSS session service. See data_enhanced in X-Pack for the search session service.
|
||||
*/
|
||||
export class SessionService implements ISessionService {
|
||||
constructor() {}
|
||||
|
|
|
@ -204,7 +204,7 @@ function discoverController($element, $route, $scope, $timeout, Promise, uiCapab
|
|||
};
|
||||
|
||||
const history = getHistory();
|
||||
// used for restoring background session
|
||||
// used for restoring a search session
|
||||
let isInitialSearch = true;
|
||||
|
||||
// search session requested a data refresh
|
||||
|
|
|
@ -12,8 +12,8 @@ export {
|
|||
EqlSearchStrategyResponse,
|
||||
IAsyncSearchOptions,
|
||||
pollSearch,
|
||||
BackgroundSessionSavedObjectAttributes,
|
||||
BackgroundSessionFindOptions,
|
||||
BackgroundSessionStatus,
|
||||
BackgroundSessionSearchInfo,
|
||||
SearchSessionSavedObjectAttributes,
|
||||
SearchSessionFindOptions,
|
||||
SearchSessionStatus,
|
||||
SearchSessionRequestInfo,
|
||||
} from './search';
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export enum BackgroundSessionStatus {
|
||||
export enum SearchSessionStatus {
|
||||
IN_PROGRESS = 'in_progress',
|
||||
ERROR = 'error',
|
||||
COMPLETE = 'complete',
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export interface BackgroundSessionSavedObjectAttributes {
|
||||
export interface SearchSessionSavedObjectAttributes {
|
||||
/**
|
||||
* User-facing session name to be displayed in session management
|
||||
*/
|
||||
|
@ -19,15 +19,15 @@ export interface BackgroundSessionSavedObjectAttributes {
|
|||
urlGeneratorId: string;
|
||||
initialState: Record<string, unknown>;
|
||||
restoreState: Record<string, unknown>;
|
||||
idMapping: Record<string, BackgroundSessionSearchInfo>;
|
||||
idMapping: Record<string, SearchSessionRequestInfo>;
|
||||
}
|
||||
|
||||
export interface BackgroundSessionSearchInfo {
|
||||
export interface SearchSessionRequestInfo {
|
||||
id: string; // ID of the async search request
|
||||
strategy: string; // Search strategy used to submit the search request
|
||||
}
|
||||
|
||||
export interface BackgroundSessionFindOptions {
|
||||
export interface SearchSessionFindOptions {
|
||||
page?: number;
|
||||
perPage?: number;
|
||||
sortField?: string;
|
||||
|
|
|
@ -13,7 +13,7 @@ import { setAutocompleteService } from './services';
|
|||
import { setupKqlQuerySuggestionProvider, KUERY_LANGUAGE_NAME } from './autocomplete';
|
||||
import { EnhancedSearchInterceptor } from './search/search_interceptor';
|
||||
import { toMountPoint } from '../../../../src/plugins/kibana_react/public';
|
||||
import { createConnectedBackgroundSessionIndicator } from './search';
|
||||
import { createConnectedSearchSessionIndicator } from './search';
|
||||
import { ConfigSchema } from '../config';
|
||||
|
||||
export interface DataEnhancedSetupDependencies {
|
||||
|
@ -66,7 +66,7 @@ export class DataEnhancedPlugin
|
|||
core.chrome.setBreadcrumbsAppendExtension({
|
||||
content: toMountPoint(
|
||||
React.createElement(
|
||||
createConnectedBackgroundSessionIndicator({
|
||||
createConnectedSearchSessionIndicator({
|
||||
sessionService: plugins.data.search.session,
|
||||
application: core.application,
|
||||
timeFilter: plugins.data.query.timefilter.timefilter,
|
||||
|
|
|
@ -9,7 +9,7 @@ import { EnhancedSearchInterceptor } from './search_interceptor';
|
|||
import { CoreSetup, CoreStart } from 'kibana/public';
|
||||
import { UI_SETTINGS } from '../../../../../src/plugins/data/common';
|
||||
import { AbortError } from '../../../../../src/plugins/kibana_utils/public';
|
||||
import { ISessionService, SearchTimeoutError, SessionState } from 'src/plugins/data/public';
|
||||
import { ISessionService, SearchTimeoutError, SearchSessionState } from 'src/plugins/data/public';
|
||||
import { dataPluginMock } from '../../../../../src/plugins/data/public/mocks';
|
||||
import { bfetchPluginMock } from '../../../../../src/plugins/bfetch/public/mocks';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
|
@ -45,12 +45,12 @@ function mockFetchImplementation(responses: any[]) {
|
|||
describe('EnhancedSearchInterceptor', () => {
|
||||
let mockUsageCollector: any;
|
||||
let sessionService: jest.Mocked<ISessionService>;
|
||||
let sessionState$: BehaviorSubject<SessionState>;
|
||||
let sessionState$: BehaviorSubject<SearchSessionState>;
|
||||
|
||||
beforeEach(() => {
|
||||
mockCoreSetup = coreMock.createSetup();
|
||||
mockCoreStart = coreMock.createStart();
|
||||
sessionState$ = new BehaviorSubject<SessionState>(SessionState.None);
|
||||
sessionState$ = new BehaviorSubject<SearchSessionState>(SearchSessionState.None);
|
||||
const dataPluginMockStart = dataPluginMock.createStartContract();
|
||||
sessionService = {
|
||||
...(dataPluginMockStart.search.session as jest.Mocked<ISessionService>),
|
||||
|
@ -408,7 +408,7 @@ describe('EnhancedSearchInterceptor', () => {
|
|||
expect(next).toHaveBeenCalled();
|
||||
expect(error).not.toHaveBeenCalled();
|
||||
|
||||
sessionState$.next(SessionState.BackgroundLoading);
|
||||
sessionState$.next(SearchSessionState.BackgroundLoading);
|
||||
|
||||
await timeTravel(240);
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ import {
|
|||
SearchInterceptorDeps,
|
||||
UI_SETTINGS,
|
||||
IKibanaSearchRequest,
|
||||
SessionState,
|
||||
SearchSessionState,
|
||||
} from '../../../../../src/plugins/data/public';
|
||||
import { AbortError } from '../../../../../src/plugins/kibana_utils/common';
|
||||
import { ENHANCED_ES_SEARCH_STRATEGY, IAsyncSearchOptions, pollSearch } from '../../common';
|
||||
|
@ -77,7 +77,7 @@ export class EnhancedSearchInterceptor extends SearchInterceptor {
|
|||
this.deps.session.state$
|
||||
.pipe(
|
||||
skip(1), // ignore any state, we are only interested in transition x -> BackgroundLoading
|
||||
filter((state) => isCurrentSession() && state === SessionState.BackgroundLoading),
|
||||
filter((state) => isCurrentSession() && state === SearchSessionState.BackgroundLoading),
|
||||
take(1)
|
||||
)
|
||||
.subscribe(() => {
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import { BackgroundSessionIndicator } from './background_session_indicator';
|
||||
import { SessionState } from '../../../../../../../src/plugins/data/public';
|
||||
|
||||
storiesOf('components/BackgroundSessionIndicator', module).add('default', () => (
|
||||
<>
|
||||
<div>
|
||||
<BackgroundSessionIndicator state={SessionState.Loading} />
|
||||
</div>
|
||||
<div>
|
||||
<BackgroundSessionIndicator state={SessionState.Completed} />
|
||||
</div>
|
||||
<div>
|
||||
<BackgroundSessionIndicator state={SessionState.BackgroundLoading} />
|
||||
</div>
|
||||
<div>
|
||||
<BackgroundSessionIndicator state={SessionState.BackgroundCompleted} />
|
||||
</div>
|
||||
<div>
|
||||
<BackgroundSessionIndicator state={SessionState.Restored} />
|
||||
</div>
|
||||
<div>
|
||||
<BackgroundSessionIndicator
|
||||
state={SessionState.Completed}
|
||||
disabled={true}
|
||||
disabledReasonText={
|
||||
'Send to background capability is unavailable when auto-refresh is enabled'
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
));
|
|
@ -7,12 +7,12 @@
|
|||
import React from 'react';
|
||||
import { render, waitFor, screen, act } from '@testing-library/react';
|
||||
import { dataPluginMock } from '../../../../../../../src/plugins/data/public/mocks';
|
||||
import { createConnectedBackgroundSessionIndicator } from './connected_background_session_indicator';
|
||||
import { createConnectedSearchSessionIndicator } from './connected_search_session_indicator';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import {
|
||||
ISessionService,
|
||||
RefreshInterval,
|
||||
SessionState,
|
||||
SearchSessionState,
|
||||
TimefilterContract,
|
||||
} from '../../../../../../../src/plugins/data/public';
|
||||
import { coreMock } from '../../../../../../../src/core/public/mocks';
|
||||
|
@ -31,78 +31,76 @@ beforeEach(() => {
|
|||
});
|
||||
|
||||
test("shouldn't show indicator in case no active search session", async () => {
|
||||
const BackgroundSessionIndicator = createConnectedBackgroundSessionIndicator({
|
||||
const SearchSessionIndicator = createConnectedSearchSessionIndicator({
|
||||
sessionService,
|
||||
application: coreStart.application,
|
||||
timeFilter,
|
||||
});
|
||||
const { getByTestId, container } = render(<BackgroundSessionIndicator />);
|
||||
const { getByTestId, container } = render(<SearchSessionIndicator />);
|
||||
|
||||
// make sure `backgroundSessionIndicator` isn't appearing after some time (lazy-loading)
|
||||
// make sure `searchSessionIndicator` isn't appearing after some time (lazy-loading)
|
||||
await expect(
|
||||
waitFor(() => getByTestId('backgroundSessionIndicator'), { timeout: 100 })
|
||||
waitFor(() => getByTestId('searchSessionIndicator'), { timeout: 100 })
|
||||
).rejects.toThrow();
|
||||
expect(container).toMatchInlineSnapshot(`<div />`);
|
||||
});
|
||||
|
||||
test('should show indicator in case there is an active search session', async () => {
|
||||
const state$ = new BehaviorSubject(SessionState.Loading);
|
||||
const BackgroundSessionIndicator = createConnectedBackgroundSessionIndicator({
|
||||
const state$ = new BehaviorSubject(SearchSessionState.Loading);
|
||||
const SearchSessionIndicator = createConnectedSearchSessionIndicator({
|
||||
sessionService: { ...sessionService, state$ },
|
||||
application: coreStart.application,
|
||||
timeFilter,
|
||||
});
|
||||
const { getByTestId } = render(<BackgroundSessionIndicator />);
|
||||
const { getByTestId } = render(<SearchSessionIndicator />);
|
||||
|
||||
await waitFor(() => getByTestId('backgroundSessionIndicator'));
|
||||
await waitFor(() => getByTestId('searchSessionIndicator'));
|
||||
});
|
||||
|
||||
test('should be disabled when permissions are off', async () => {
|
||||
const state$ = new BehaviorSubject(SessionState.Loading);
|
||||
const state$ = new BehaviorSubject(SearchSessionState.Loading);
|
||||
coreStart.application.currentAppId$ = new BehaviorSubject('discover');
|
||||
(coreStart.application.capabilities as any) = {
|
||||
discover: {
|
||||
storeSearchSession: false,
|
||||
},
|
||||
};
|
||||
const BackgroundSessionIndicator = createConnectedBackgroundSessionIndicator({
|
||||
const SearchSessionIndicator = createConnectedSearchSessionIndicator({
|
||||
sessionService: { ...sessionService, state$ },
|
||||
application: coreStart.application,
|
||||
timeFilter,
|
||||
});
|
||||
|
||||
render(<BackgroundSessionIndicator />);
|
||||
render(<SearchSessionIndicator />);
|
||||
|
||||
await waitFor(() => screen.getByTestId('backgroundSessionIndicator'));
|
||||
await waitFor(() => screen.getByTestId('searchSessionIndicator'));
|
||||
|
||||
expect(screen.getByTestId('backgroundSessionIndicator').querySelector('button')).toBeDisabled();
|
||||
expect(screen.getByTestId('searchSessionIndicator').querySelector('button')).toBeDisabled();
|
||||
});
|
||||
|
||||
test('should be disabled during auto-refresh', async () => {
|
||||
const state$ = new BehaviorSubject(SessionState.Loading);
|
||||
const state$ = new BehaviorSubject(SearchSessionState.Loading);
|
||||
coreStart.application.currentAppId$ = new BehaviorSubject('discover');
|
||||
(coreStart.application.capabilities as any) = {
|
||||
discover: {
|
||||
storeSearchSession: true,
|
||||
},
|
||||
};
|
||||
const BackgroundSessionIndicator = createConnectedBackgroundSessionIndicator({
|
||||
const SearchSessionIndicator = createConnectedSearchSessionIndicator({
|
||||
sessionService: { ...sessionService, state$ },
|
||||
application: coreStart.application,
|
||||
timeFilter,
|
||||
});
|
||||
|
||||
render(<BackgroundSessionIndicator />);
|
||||
render(<SearchSessionIndicator />);
|
||||
|
||||
await waitFor(() => screen.getByTestId('backgroundSessionIndicator'));
|
||||
await waitFor(() => screen.getByTestId('searchSessionIndicator'));
|
||||
|
||||
expect(
|
||||
screen.getByTestId('backgroundSessionIndicator').querySelector('button')
|
||||
).not.toBeDisabled();
|
||||
expect(screen.getByTestId('searchSessionIndicator').querySelector('button')).not.toBeDisabled();
|
||||
|
||||
act(() => {
|
||||
refreshInterval$.next({ value: 0, pause: false });
|
||||
});
|
||||
|
||||
expect(screen.getByTestId('backgroundSessionIndicator').querySelector('button')).toBeDisabled();
|
||||
expect(screen.getByTestId('searchSessionIndicator').querySelector('button')).toBeDisabled();
|
||||
});
|
|
@ -8,22 +8,22 @@ import React from 'react';
|
|||
import { debounceTime, distinctUntilChanged, map } from 'rxjs/operators';
|
||||
import useObservable from 'react-use/lib/useObservable';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { BackgroundSessionIndicator } from '../background_session_indicator';
|
||||
import { SearchSessionIndicator } from '../search_session_indicator';
|
||||
import { ISessionService, TimefilterContract } from '../../../../../../../src/plugins/data/public/';
|
||||
import { RedirectAppLinks } from '../../../../../../../src/plugins/kibana_react/public';
|
||||
import { ApplicationStart } from '../../../../../../../src/core/public';
|
||||
|
||||
export interface BackgroundSessionIndicatorDeps {
|
||||
export interface SearchSessionIndicatorDeps {
|
||||
sessionService: ISessionService;
|
||||
timeFilter: TimefilterContract;
|
||||
application: ApplicationStart;
|
||||
}
|
||||
|
||||
export const createConnectedBackgroundSessionIndicator = ({
|
||||
export const createConnectedSearchSessionIndicator = ({
|
||||
sessionService,
|
||||
application,
|
||||
timeFilter,
|
||||
}: BackgroundSessionIndicatorDeps): React.FC => {
|
||||
}: SearchSessionIndicatorDeps): React.FC => {
|
||||
const isAutoRefreshEnabled = () => !timeFilter.getRefreshInterval().pause;
|
||||
const isAutoRefreshEnabled$ = timeFilter
|
||||
.getRefreshIntervalUpdate$()
|
||||
|
@ -53,7 +53,7 @@ export const createConnectedBackgroundSessionIndicator = ({
|
|||
|
||||
if (getCapabilitiesByAppId(application.capabilities, appId)?.storeSearchSession !== true) {
|
||||
disabled = true;
|
||||
disabledReasonText = i18n.translate('xpack.data.backgroundSessionIndicator.noCapability', {
|
||||
disabledReasonText = i18n.translate('xpack.data.searchSessionIndicator.noCapability', {
|
||||
defaultMessage: "You don't have permissions to send to background.",
|
||||
});
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ export const createConnectedBackgroundSessionIndicator = ({
|
|||
if (autoRefreshEnabled) {
|
||||
disabled = true;
|
||||
disabledReasonText = i18n.translate(
|
||||
'xpack.data.backgroundSessionIndicator.disabledDueToAutoRefreshMessage',
|
||||
'xpack.data.searchSessionIndicator.disabledDueToAutoRefreshMessage',
|
||||
{
|
||||
defaultMessage: 'Send to background is not available when auto refresh is enabled.',
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ export const createConnectedBackgroundSessionIndicator = ({
|
|||
if (!state) return null;
|
||||
return (
|
||||
<RedirectAppLinks application={application}>
|
||||
<BackgroundSessionIndicator
|
||||
<SearchSessionIndicator
|
||||
state={state}
|
||||
onContinueInBackground={() => {
|
||||
sessionService.save();
|
|
@ -5,6 +5,6 @@
|
|||
*/
|
||||
|
||||
export {
|
||||
BackgroundSessionIndicatorDeps,
|
||||
createConnectedBackgroundSessionIndicator,
|
||||
} from './connected_background_session_indicator';
|
||||
SearchSessionIndicatorDeps,
|
||||
createConnectedSearchSessionIndicator,
|
||||
} from './connected_search_session_indicator';
|
|
@ -4,14 +4,14 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export enum BackgroundSessionViewState {
|
||||
export enum SearchSessionViewState {
|
||||
/**
|
||||
* Pending search request has not been sent to the background yet
|
||||
*/
|
||||
Loading = 'loading',
|
||||
|
||||
/**
|
||||
* No action was taken and the page completed loading without background session creation.
|
||||
* No action was taken and the page completed loading without search session creation.
|
||||
*/
|
||||
Completed = 'completed',
|
||||
|
||||
|
@ -22,7 +22,7 @@ export enum BackgroundSessionViewState {
|
|||
BackgroundLoading = 'backgroundLoading',
|
||||
|
||||
/**
|
||||
* Page load completed with background session created.
|
||||
* Page load completed with search session created.
|
||||
*/
|
||||
BackgroundCompleted = 'backgroundCompleted',
|
||||
|
|
@ -4,4 +4,4 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export * from './connected_background_session_indicator';
|
||||
export * from './connected_search_session_indicator';
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
|
||||
import { EuiDelayRender, EuiLoadingSpinner } from '@elastic/eui';
|
||||
import React from 'react';
|
||||
import type { BackgroundSessionIndicatorProps } from './background_session_indicator';
|
||||
export type { BackgroundSessionIndicatorProps };
|
||||
import type { SearchSessionIndicatorProps } from './search_session_indicator';
|
||||
export type { SearchSessionIndicatorProps };
|
||||
|
||||
const Fallback = () => (
|
||||
<EuiDelayRender>
|
||||
|
@ -15,9 +15,9 @@ const Fallback = () => (
|
|||
</EuiDelayRender>
|
||||
);
|
||||
|
||||
const LazyBackgroundSessionIndicator = React.lazy(() => import('./background_session_indicator'));
|
||||
export const BackgroundSessionIndicator = (props: BackgroundSessionIndicatorProps) => (
|
||||
const LazySearchSessionIndicator = React.lazy(() => import('./search_session_indicator'));
|
||||
export const SearchSessionIndicator = (props: SearchSessionIndicatorProps) => (
|
||||
<React.Suspense fallback={<Fallback />}>
|
||||
<LazyBackgroundSessionIndicator {...props} />
|
||||
<LazySearchSessionIndicator {...props} />
|
||||
</React.Suspense>
|
||||
);
|
|
@ -1,14 +1,14 @@
|
|||
.backgroundSessionIndicator {
|
||||
.searchSessionIndicator {
|
||||
padding: 0 $euiSizeXS;
|
||||
}
|
||||
|
||||
@include euiBreakpoint('xs', 's') {
|
||||
.backgroundSessionIndicator__popoverContainer.euiFlexGroup--responsive .euiFlexItem {
|
||||
.searchSessionIndicator__popoverContainer.euiFlexGroup--responsive .euiFlexItem {
|
||||
margin-bottom: $euiSizeXS !important;
|
||||
}
|
||||
}
|
||||
|
||||
.backgroundSessionIndicator__verticalDivider {
|
||||
.searchSessionIndicator__verticalDivider {
|
||||
@include euiBreakpoint('xs', 's') {
|
||||
margin-left: $euiSizeXS;
|
||||
padding-left: $euiSizeXS;
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import { SearchSessionIndicator } from './search_session_indicator';
|
||||
import { SearchSessionState } from '../../../../../../../src/plugins/data/public';
|
||||
|
||||
storiesOf('components/SearchSessionIndicator', module).add('default', () => (
|
||||
<>
|
||||
<div>
|
||||
<SearchSessionIndicator state={SearchSessionState.Loading} />
|
||||
</div>
|
||||
<div>
|
||||
<SearchSessionIndicator state={SearchSessionState.Completed} />
|
||||
</div>
|
||||
<div>
|
||||
<SearchSessionIndicator state={SearchSessionState.BackgroundLoading} />
|
||||
</div>
|
||||
<div>
|
||||
<SearchSessionIndicator state={SearchSessionState.BackgroundCompleted} />
|
||||
</div>
|
||||
<div>
|
||||
<SearchSessionIndicator state={SearchSessionState.Restored} />
|
||||
</div>
|
||||
<div>
|
||||
<SearchSessionIndicator
|
||||
state={SearchSessionState.Completed}
|
||||
disabled={true}
|
||||
disabledReasonText={
|
||||
'Send to background capability is unavailable when auto-refresh is enabled'
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
));
|
|
@ -7,9 +7,9 @@
|
|||
import React, { ReactNode } from 'react';
|
||||
import { screen, render } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { BackgroundSessionIndicator } from './background_session_indicator';
|
||||
import { SearchSessionIndicator } from './search_session_indicator';
|
||||
import { IntlProvider } from 'react-intl';
|
||||
import { SessionState } from '../../../../../../../src/plugins/data/public';
|
||||
import { SearchSessionState } from '../../../../../../../src/plugins/data/public';
|
||||
|
||||
function Container({ children }: { children?: ReactNode }) {
|
||||
return <IntlProvider locale="en">{children}</IntlProvider>;
|
||||
|
@ -19,12 +19,12 @@ test('Loading state', async () => {
|
|||
const onCancel = jest.fn();
|
||||
render(
|
||||
<Container>
|
||||
<BackgroundSessionIndicator state={SessionState.Loading} onCancel={onCancel} />
|
||||
<SearchSessionIndicator state={SearchSessionState.Loading} onCancel={onCancel} />
|
||||
</Container>
|
||||
);
|
||||
|
||||
await userEvent.click(screen.getByLabelText('Loading results'));
|
||||
await userEvent.click(screen.getByText('Cancel'));
|
||||
await userEvent.click(screen.getByLabelText('Loading'));
|
||||
await userEvent.click(screen.getByText('Cancel session'));
|
||||
|
||||
expect(onCancel).toBeCalled();
|
||||
});
|
||||
|
@ -33,12 +33,12 @@ test('Completed state', async () => {
|
|||
const onSave = jest.fn();
|
||||
render(
|
||||
<Container>
|
||||
<BackgroundSessionIndicator state={SessionState.Completed} onSaveResults={onSave} />
|
||||
<SearchSessionIndicator state={SearchSessionState.Completed} onSaveResults={onSave} />
|
||||
</Container>
|
||||
);
|
||||
|
||||
await userEvent.click(screen.getByLabelText('Results loaded'));
|
||||
await userEvent.click(screen.getByText('Save'));
|
||||
await userEvent.click(screen.getByLabelText('Loaded'));
|
||||
await userEvent.click(screen.getByText('Save session'));
|
||||
|
||||
expect(onSave).toBeCalled();
|
||||
});
|
||||
|
@ -47,12 +47,12 @@ test('Loading in the background state', async () => {
|
|||
const onCancel = jest.fn();
|
||||
render(
|
||||
<Container>
|
||||
<BackgroundSessionIndicator state={SessionState.BackgroundLoading} onCancel={onCancel} />
|
||||
<SearchSessionIndicator state={SearchSessionState.BackgroundLoading} onCancel={onCancel} />
|
||||
</Container>
|
||||
);
|
||||
|
||||
await userEvent.click(screen.getByLabelText('Loading results in the background'));
|
||||
await userEvent.click(screen.getByText('Cancel'));
|
||||
await userEvent.click(screen.getByText('Cancel session'));
|
||||
|
||||
expect(onCancel).toBeCalled();
|
||||
});
|
||||
|
@ -60,15 +60,15 @@ test('Loading in the background state', async () => {
|
|||
test('BackgroundCompleted state', async () => {
|
||||
render(
|
||||
<Container>
|
||||
<BackgroundSessionIndicator
|
||||
state={SessionState.BackgroundCompleted}
|
||||
viewBackgroundSessionsLink={'__link__'}
|
||||
<SearchSessionIndicator
|
||||
state={SearchSessionState.BackgroundCompleted}
|
||||
viewSearchSessionsLink={'__link__'}
|
||||
/>
|
||||
</Container>
|
||||
);
|
||||
|
||||
await userEvent.click(screen.getByLabelText('Results loaded in the background'));
|
||||
expect(screen.getByRole('link', { name: 'View background sessions' }).getAttribute('href')).toBe(
|
||||
expect(screen.getByRole('link', { name: 'View all sessions' }).getAttribute('href')).toBe(
|
||||
'__link__'
|
||||
);
|
||||
});
|
||||
|
@ -77,7 +77,7 @@ test('Restored state', async () => {
|
|||
const onRefresh = jest.fn();
|
||||
render(
|
||||
<Container>
|
||||
<BackgroundSessionIndicator state={SessionState.Restored} onRefresh={onRefresh} />
|
||||
<SearchSessionIndicator state={SearchSessionState.Restored} onRefresh={onRefresh} />
|
||||
</Container>
|
||||
);
|
||||
|
||||
|
@ -91,7 +91,7 @@ test('Canceled state', async () => {
|
|||
const onRefresh = jest.fn();
|
||||
render(
|
||||
<Container>
|
||||
<BackgroundSessionIndicator state={SessionState.Canceled} onRefresh={onRefresh} />
|
||||
<SearchSessionIndicator state={SearchSessionState.Canceled} onRefresh={onRefresh} />
|
||||
</Container>
|
||||
);
|
||||
|
||||
|
@ -104,9 +104,9 @@ test('Canceled state', async () => {
|
|||
test('Disabled state', async () => {
|
||||
render(
|
||||
<Container>
|
||||
<BackgroundSessionIndicator state={SessionState.Loading} disabled={true} />
|
||||
<SearchSessionIndicator state={SearchSessionState.Loading} disabled={true} />
|
||||
</Container>
|
||||
);
|
||||
|
||||
expect(screen.getByTestId('backgroundSessionIndicator').querySelector('button')).toBeDisabled();
|
||||
expect(screen.getByTestId('searchSessionIndicator').querySelector('button')).toBeDisabled();
|
||||
});
|
|
@ -20,31 +20,31 @@ import {
|
|||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import './background_session_indicator.scss';
|
||||
import { SessionState } from '../../../../../../../src/plugins/data/public/';
|
||||
import './search_session_indicator.scss';
|
||||
import { SearchSessionState } from '../../../../../../../src/plugins/data/public';
|
||||
|
||||
export interface BackgroundSessionIndicatorProps {
|
||||
state: SessionState;
|
||||
export interface SearchSessionIndicatorProps {
|
||||
state: SearchSessionState;
|
||||
onContinueInBackground?: () => void;
|
||||
onCancel?: () => void;
|
||||
viewBackgroundSessionsLink?: string;
|
||||
viewSearchSessionsLink?: string;
|
||||
onSaveResults?: () => void;
|
||||
onRefresh?: () => void;
|
||||
disabled?: boolean;
|
||||
disabledReasonText?: string;
|
||||
}
|
||||
|
||||
type ActionButtonProps = BackgroundSessionIndicatorProps & { buttonProps: EuiButtonEmptyProps };
|
||||
type ActionButtonProps = SearchSessionIndicatorProps & { buttonProps: EuiButtonEmptyProps };
|
||||
|
||||
const CancelButton = ({ onCancel = () => {}, buttonProps = {} }: ActionButtonProps) => (
|
||||
<EuiButtonEmpty
|
||||
onClick={onCancel}
|
||||
data-test-subj={'backgroundSessionIndicatorCancelBtn'}
|
||||
data-test-subj={'searchSessionIndicatorCancelBtn'}
|
||||
{...buttonProps}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.data.backgroundSessionIndicator.cancelButtonText"
|
||||
defaultMessage="Cancel"
|
||||
id="xpack.data.searchSessionIndicator.cancelButtonText"
|
||||
defaultMessage="Cancel session"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
);
|
||||
|
@ -55,28 +55,28 @@ const ContinueInBackgroundButton = ({
|
|||
}: ActionButtonProps) => (
|
||||
<EuiButtonEmpty
|
||||
onClick={onContinueInBackground}
|
||||
data-test-subj={'backgroundSessionIndicatorContinueInBackgroundBtn'}
|
||||
data-test-subj={'searchSessionIndicatorContinueInBackgroundBtn'}
|
||||
{...buttonProps}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.data.backgroundSessionIndicator.continueInBackgroundButtonText"
|
||||
id="xpack.data.searchSessionIndicator.continueInBackgroundButtonText"
|
||||
defaultMessage="Continue in background"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
);
|
||||
|
||||
const ViewBackgroundSessionsButton = ({
|
||||
viewBackgroundSessionsLink = 'management',
|
||||
const ViewAllSearchSessionsButton = ({
|
||||
viewSearchSessionsLink = 'management',
|
||||
buttonProps = {},
|
||||
}: ActionButtonProps) => (
|
||||
<EuiButtonEmpty
|
||||
href={viewBackgroundSessionsLink}
|
||||
data-test-subj={'backgroundSessionIndicatorViewBackgroundSessionsLink'}
|
||||
href={viewSearchSessionsLink}
|
||||
data-test-subj={'searchSessionIndicatorviewSearchSessionsLink'}
|
||||
{...buttonProps}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.data.backgroundSessionIndicator.viewBackgroundSessionsLinkText"
|
||||
defaultMessage="View background sessions"
|
||||
id="xpack.data.searchSessionIndicator.viewSearchSessionsLinkText"
|
||||
defaultMessage="View all sessions"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
);
|
||||
|
@ -84,11 +84,11 @@ const ViewBackgroundSessionsButton = ({
|
|||
const RefreshButton = ({ onRefresh = () => {}, buttonProps = {} }: ActionButtonProps) => (
|
||||
<EuiButtonEmpty
|
||||
onClick={onRefresh}
|
||||
data-test-subj={'backgroundSessionIndicatorRefreshBtn'}
|
||||
data-test-subj={'searchSessionIndicatorRefreshBtn'}
|
||||
{...buttonProps}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.data.backgroundSessionIndicator.refreshButtonText"
|
||||
id="xpack.data.searchSessionIndicator.refreshButtonText"
|
||||
defaultMessage="Refresh"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
|
@ -97,18 +97,18 @@ const RefreshButton = ({ onRefresh = () => {}, buttonProps = {} }: ActionButtonP
|
|||
const SaveButton = ({ onSaveResults = () => {}, buttonProps = {} }: ActionButtonProps) => (
|
||||
<EuiButtonEmpty
|
||||
onClick={onSaveResults}
|
||||
data-test-subj={'backgroundSessionIndicatorSaveBtn'}
|
||||
data-test-subj={'searchSessionIndicatorSaveBtn'}
|
||||
{...buttonProps}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.data.backgroundSessionIndicator.saveButtonText"
|
||||
defaultMessage="Save"
|
||||
id="xpack.data.searchSessionIndicator.saveButtonText"
|
||||
defaultMessage="Save session"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
);
|
||||
|
||||
const backgroundSessionIndicatorViewStateToProps: {
|
||||
[state in SessionState]: {
|
||||
const searchSessionIndicatorViewStateToProps: {
|
||||
[state in SearchSessionState]: {
|
||||
button: Pick<EuiButtonIconProps, 'color' | 'iconType' | 'aria-label'> & {
|
||||
tooltipText: string;
|
||||
};
|
||||
|
@ -119,162 +119,151 @@ const backgroundSessionIndicatorViewStateToProps: {
|
|||
};
|
||||
} | null;
|
||||
} = {
|
||||
[SessionState.None]: null,
|
||||
[SessionState.Loading]: {
|
||||
[SearchSessionState.None]: null,
|
||||
[SearchSessionState.Loading]: {
|
||||
button: {
|
||||
color: 'subdued',
|
||||
iconType: 'clock',
|
||||
'aria-label': i18n.translate(
|
||||
'xpack.data.backgroundSessionIndicator.loadingResultsIconAriaLabel',
|
||||
{ defaultMessage: 'Loading results' }
|
||||
'xpack.data.searchSessionIndicator.loadingResultsIconAriaLabel',
|
||||
{ defaultMessage: 'Loading' }
|
||||
),
|
||||
tooltipText: i18n.translate(
|
||||
'xpack.data.backgroundSessionIndicator.loadingResultsIconTooltipText',
|
||||
{ defaultMessage: 'Loading results' }
|
||||
'xpack.data.searchSessionIndicator.loadingResultsIconTooltipText',
|
||||
{ defaultMessage: 'Loading' }
|
||||
),
|
||||
},
|
||||
popover: {
|
||||
text: i18n.translate('xpack.data.backgroundSessionIndicator.loadingResultsText', {
|
||||
text: i18n.translate('xpack.data.searchSessionIndicator.loadingResultsText', {
|
||||
defaultMessage: 'Loading',
|
||||
}),
|
||||
primaryAction: CancelButton,
|
||||
secondaryAction: ContinueInBackgroundButton,
|
||||
},
|
||||
},
|
||||
[SessionState.Completed]: {
|
||||
[SearchSessionState.Completed]: {
|
||||
button: {
|
||||
color: 'subdued',
|
||||
iconType: 'checkInCircleFilled',
|
||||
'aria-label': i18n.translate(
|
||||
'xpack.data.backgroundSessionIndicator.resultsLoadedIconAriaLabel',
|
||||
{
|
||||
defaultMessage: 'Results loaded',
|
||||
}
|
||||
),
|
||||
'aria-label': i18n.translate('xpack.data.searchSessionIndicator.resultsLoadedIconAriaLabel', {
|
||||
defaultMessage: 'Loaded',
|
||||
}),
|
||||
tooltipText: i18n.translate(
|
||||
'xpack.data.backgroundSessionIndicator.resultsLoadedIconTooltipText',
|
||||
'xpack.data.searchSessionIndicator.resultsLoadedIconTooltipText',
|
||||
{
|
||||
defaultMessage: 'Results loaded',
|
||||
}
|
||||
),
|
||||
},
|
||||
popover: {
|
||||
text: i18n.translate('xpack.data.backgroundSessionIndicator.resultsLoadedText', {
|
||||
defaultMessage: 'Results loaded',
|
||||
text: i18n.translate('xpack.data.searchSessionIndicator.resultsLoadedText', {
|
||||
defaultMessage: 'Loaded',
|
||||
}),
|
||||
primaryAction: SaveButton,
|
||||
secondaryAction: ViewBackgroundSessionsButton,
|
||||
secondaryAction: ViewAllSearchSessionsButton,
|
||||
},
|
||||
},
|
||||
[SessionState.BackgroundLoading]: {
|
||||
[SearchSessionState.BackgroundLoading]: {
|
||||
button: {
|
||||
iconType: EuiLoadingSpinner,
|
||||
'aria-label': i18n.translate(
|
||||
'xpack.data.backgroundSessionIndicator.loadingInTheBackgroundIconAriaLabel',
|
||||
'xpack.data.searchSessionIndicator.loadingInTheBackgroundIconAriaLabel',
|
||||
{
|
||||
defaultMessage: 'Loading results in the background',
|
||||
}
|
||||
),
|
||||
tooltipText: i18n.translate(
|
||||
'xpack.data.backgroundSessionIndicator.loadingInTheBackgroundIconTooltipText',
|
||||
'xpack.data.searchSessionIndicator.loadingInTheBackgroundIconTooltipText',
|
||||
{
|
||||
defaultMessage: 'Loading results in the background',
|
||||
}
|
||||
),
|
||||
},
|
||||
popover: {
|
||||
text: i18n.translate('xpack.data.backgroundSessionIndicator.loadingInTheBackgroundText', {
|
||||
text: i18n.translate('xpack.data.searchSessionIndicator.loadingInTheBackgroundText', {
|
||||
defaultMessage: 'Loading in the background',
|
||||
}),
|
||||
primaryAction: CancelButton,
|
||||
secondaryAction: ViewBackgroundSessionsButton,
|
||||
secondaryAction: ViewAllSearchSessionsButton,
|
||||
},
|
||||
},
|
||||
[SessionState.BackgroundCompleted]: {
|
||||
[SearchSessionState.BackgroundCompleted]: {
|
||||
button: {
|
||||
color: 'success',
|
||||
iconType: 'checkInCircleFilled',
|
||||
'aria-label': i18n.translate(
|
||||
'xpack.data.backgroundSessionIndicator.resultLoadedInTheBackgroundIconAraText',
|
||||
'xpack.data.searchSessionIndicator.resultLoadedInTheBackgroundIconAraText',
|
||||
{
|
||||
defaultMessage: 'Results loaded in the background',
|
||||
}
|
||||
),
|
||||
tooltipText: i18n.translate(
|
||||
'xpack.data.backgroundSessionIndicator.resultLoadedInTheBackgroundIconTooltipText',
|
||||
'xpack.data.searchSessionIndicator.resultLoadedInTheBackgroundIconTooltipText',
|
||||
{
|
||||
defaultMessage: 'Results loaded in the background',
|
||||
}
|
||||
),
|
||||
},
|
||||
popover: {
|
||||
text: i18n.translate(
|
||||
'xpack.data.backgroundSessionIndicator.resultLoadedInTheBackgroundText',
|
||||
{
|
||||
defaultMessage: 'Results loaded',
|
||||
}
|
||||
),
|
||||
primaryAction: ViewBackgroundSessionsButton,
|
||||
text: i18n.translate('xpack.data.searchSessionIndicator.resultLoadedInTheBackgroundText', {
|
||||
defaultMessage: 'Loaded',
|
||||
}),
|
||||
primaryAction: ViewAllSearchSessionsButton,
|
||||
},
|
||||
},
|
||||
[SessionState.Restored]: {
|
||||
[SearchSessionState.Restored]: {
|
||||
button: {
|
||||
color: 'warning',
|
||||
iconType: 'refresh',
|
||||
'aria-label': i18n.translate(
|
||||
'xpack.data.backgroundSessionIndicator.restoredResultsIconAriaLabel',
|
||||
{
|
||||
defaultMessage: 'Results no longer current',
|
||||
}
|
||||
),
|
||||
tooltipText: i18n.translate(
|
||||
'xpack.data.backgroundSessionIndicator.restoredResultsTooltipText',
|
||||
'xpack.data.searchSessionIndicator.restoredResultsIconAriaLabel',
|
||||
{
|
||||
defaultMessage: 'Results no longer current',
|
||||
}
|
||||
),
|
||||
tooltipText: i18n.translate('xpack.data.searchSessionIndicator.restoredResultsTooltipText', {
|
||||
defaultMessage: 'Results no longer current',
|
||||
}),
|
||||
},
|
||||
popover: {
|
||||
text: i18n.translate('xpack.data.backgroundSessionIndicator.restoredText', {
|
||||
text: i18n.translate('xpack.data.searchSessionIndicator.restoredText', {
|
||||
defaultMessage: 'Results no longer current',
|
||||
}),
|
||||
primaryAction: RefreshButton,
|
||||
secondaryAction: ViewBackgroundSessionsButton,
|
||||
secondaryAction: ViewAllSearchSessionsButton,
|
||||
},
|
||||
},
|
||||
[SessionState.Canceled]: {
|
||||
[SearchSessionState.Canceled]: {
|
||||
button: {
|
||||
color: 'subdued',
|
||||
iconType: 'refresh',
|
||||
'aria-label': i18n.translate('xpack.data.backgroundSessionIndicator.canceledIconAriaLabel', {
|
||||
'aria-label': i18n.translate('xpack.data.searchSessionIndicator.canceledIconAriaLabel', {
|
||||
defaultMessage: 'Canceled',
|
||||
}),
|
||||
tooltipText: i18n.translate('xpack.data.backgroundSessionIndicator.canceledTooltipText', {
|
||||
tooltipText: i18n.translate('xpack.data.searchSessionIndicator.canceledTooltipText', {
|
||||
defaultMessage: 'Search was canceled',
|
||||
}),
|
||||
},
|
||||
popover: {
|
||||
text: i18n.translate('xpack.data.backgroundSessionIndicator.canceledText', {
|
||||
text: i18n.translate('xpack.data.searchSessionIndicator.canceledText', {
|
||||
defaultMessage: 'Search was canceled',
|
||||
}),
|
||||
primaryAction: RefreshButton,
|
||||
secondaryAction: ViewBackgroundSessionsButton,
|
||||
secondaryAction: ViewAllSearchSessionsButton,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const VerticalDivider: React.FC = () => (
|
||||
<div className="backgroundSessionIndicator__verticalDivider" />
|
||||
);
|
||||
const VerticalDivider: React.FC = () => <div className="searchSessionIndicator__verticalDivider" />;
|
||||
|
||||
export const BackgroundSessionIndicator: React.FC<BackgroundSessionIndicatorProps> = (props) => {
|
||||
export const SearchSessionIndicator: React.FC<SearchSessionIndicatorProps> = (props) => {
|
||||
const [isPopoverOpen, setIsPopoverOpen] = React.useState(false);
|
||||
const onButtonClick = () => setIsPopoverOpen((isOpen) => !isOpen);
|
||||
const closePopover = () => setIsPopoverOpen(false);
|
||||
|
||||
if (!backgroundSessionIndicatorViewStateToProps[props.state]) return null;
|
||||
if (!searchSessionIndicatorViewStateToProps[props.state]) return null;
|
||||
|
||||
const { button, popover } = backgroundSessionIndicatorViewStateToProps[props.state]!;
|
||||
const { button, popover } = searchSessionIndicatorViewStateToProps[props.state]!;
|
||||
|
||||
return (
|
||||
<EuiPopover
|
||||
|
@ -283,8 +272,8 @@ export const BackgroundSessionIndicator: React.FC<BackgroundSessionIndicatorProp
|
|||
closePopover={closePopover}
|
||||
anchorPosition={'rightCenter'}
|
||||
panelPaddingSize={'s'}
|
||||
className="backgroundSessionIndicator"
|
||||
data-test-subj={'backgroundSessionIndicator'}
|
||||
className="searchSessionIndicator"
|
||||
data-test-subj={'searchSessionIndicator'}
|
||||
data-state={props.state}
|
||||
button={
|
||||
<EuiToolTip content={props.disabled ? props.disabledReasonText : button.tooltipText}>
|
||||
|
@ -302,8 +291,8 @@ export const BackgroundSessionIndicator: React.FC<BackgroundSessionIndicatorProp
|
|||
responsive={true}
|
||||
alignItems={'center'}
|
||||
gutterSize={'s'}
|
||||
className="backgroundSessionIndicator__popoverContainer"
|
||||
data-test-subj={'backgroundSessionIndicatorPopoverContainer'}
|
||||
className="searchSessionIndicator__popoverContainer"
|
||||
data-test-subj={'searchSessionIndicatorPopoverContainer'}
|
||||
>
|
||||
<EuiFlexItem grow={true}>
|
||||
<EuiText size="s" color={'subdued'}>
|
||||
|
@ -332,4 +321,4 @@ export const BackgroundSessionIndicator: React.FC<BackgroundSessionIndicatorProp
|
|||
|
||||
// React.lazy() needs default:
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default BackgroundSessionIndicator;
|
||||
export default SearchSessionIndicator;
|
|
@ -13,9 +13,9 @@ import {
|
|||
import { UsageCollectionSetup } from '../../../../src/plugins/usage_collection/server';
|
||||
import { ENHANCED_ES_SEARCH_STRATEGY, EQL_SEARCH_STRATEGY } from '../common';
|
||||
import { registerSessionRoutes } from './routes';
|
||||
import { backgroundSessionMapping } from './saved_objects';
|
||||
import { searchSessionMapping } from './saved_objects';
|
||||
import {
|
||||
BackgroundSessionService,
|
||||
SearchSessionService,
|
||||
enhancedEsSearchStrategyProvider,
|
||||
eqlSearchStrategyProvider,
|
||||
} from './search';
|
||||
|
@ -28,7 +28,7 @@ interface SetupDependencies {
|
|||
|
||||
export class EnhancedDataServerPlugin implements Plugin<void, void, SetupDependencies> {
|
||||
private readonly logger: Logger;
|
||||
private sessionService!: BackgroundSessionService;
|
||||
private sessionService!: SearchSessionService;
|
||||
|
||||
constructor(private initializerContext: PluginInitializerContext) {
|
||||
this.logger = initializerContext.logger.get('data_enhanced');
|
||||
|
@ -38,7 +38,7 @@ export class EnhancedDataServerPlugin implements Plugin<void, void, SetupDepende
|
|||
const usage = deps.usageCollection ? usageProvider(core) : undefined;
|
||||
|
||||
core.uiSettings.register(getUiSettings());
|
||||
core.savedObjects.registerType(backgroundSessionMapping);
|
||||
core.savedObjects.registerType(searchSessionMapping);
|
||||
|
||||
deps.data.search.registerSearchStrategy(
|
||||
ENHANCED_ES_SEARCH_STRATEGY,
|
||||
|
@ -54,7 +54,7 @@ export class EnhancedDataServerPlugin implements Plugin<void, void, SetupDepende
|
|||
eqlSearchStrategyProvider(this.logger)
|
||||
);
|
||||
|
||||
this.sessionService = new BackgroundSessionService(this.logger);
|
||||
this.sessionService = new SearchSessionService(this.logger);
|
||||
|
||||
deps.data.__enhance({
|
||||
search: {
|
||||
|
|
|
@ -4,4 +4,4 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export * from './background_session';
|
||||
export * from './search_session';
|
||||
|
|
|
@ -6,10 +6,10 @@
|
|||
|
||||
import { SavedObjectsType } from 'kibana/server';
|
||||
|
||||
export const BACKGROUND_SESSION_TYPE = 'background-session';
|
||||
export const SEARCH_SESSION_TYPE = 'search-session';
|
||||
|
||||
export const backgroundSessionMapping: SavedObjectsType = {
|
||||
name: BACKGROUND_SESSION_TYPE,
|
||||
export const searchSessionMapping: SavedObjectsType = {
|
||||
name: SEARCH_SESSION_TYPE,
|
||||
namespaceType: 'single',
|
||||
hidden: true,
|
||||
mappings: {
|
|
@ -8,11 +8,11 @@ import { BehaviorSubject, of } from 'rxjs';
|
|||
import type { SavedObject, SavedObjectsClientContract } from 'kibana/server';
|
||||
import type { SearchStrategyDependencies } from '../../../../../../src/plugins/data/server';
|
||||
import { savedObjectsClientMock } from '../../../../../../src/core/server/mocks';
|
||||
import { BackgroundSessionStatus } from '../../../common';
|
||||
import { BACKGROUND_SESSION_TYPE } from '../../saved_objects';
|
||||
import { SearchSessionStatus } from '../../../common';
|
||||
import { SEARCH_SESSION_TYPE } from '../../saved_objects';
|
||||
import {
|
||||
BackgroundSessionDependencies,
|
||||
BackgroundSessionService,
|
||||
SearchSessionDependencies,
|
||||
SearchSessionService,
|
||||
INMEM_TRACKING_INTERVAL,
|
||||
MAX_UPDATE_RETRIES,
|
||||
SessionInfo,
|
||||
|
@ -24,9 +24,9 @@ import { ConfigSchema } from '../../../config';
|
|||
|
||||
const flushPromises = () => new Promise((resolve) => setImmediate(resolve));
|
||||
|
||||
describe('BackgroundSessionService', () => {
|
||||
describe('SearchSessionService', () => {
|
||||
let savedObjectsClient: jest.Mocked<SavedObjectsClientContract>;
|
||||
let service: BackgroundSessionService;
|
||||
let service: SearchSessionService;
|
||||
|
||||
const MOCK_SESSION_ID = 'session-id-mock';
|
||||
const MOCK_ASYNC_ID = '123456';
|
||||
|
@ -93,7 +93,7 @@ describe('BackgroundSessionService', () => {
|
|||
const sessionId = 'd7170a35-7e2c-48d6-8dec-9a056721b489';
|
||||
const mockSavedObject: SavedObject = {
|
||||
id: 'd7170a35-7e2c-48d6-8dec-9a056721b489',
|
||||
type: BACKGROUND_SESSION_TYPE,
|
||||
type: SEARCH_SESSION_TYPE,
|
||||
attributes: {
|
||||
name: 'my_name',
|
||||
appId: 'my_app_id',
|
||||
|
@ -110,7 +110,7 @@ describe('BackgroundSessionService', () => {
|
|||
warn: jest.fn(),
|
||||
error: jest.fn(),
|
||||
};
|
||||
service = new BackgroundSessionService(mockLogger);
|
||||
service = new SearchSessionService(mockLogger);
|
||||
});
|
||||
|
||||
it('search throws if `name` is not provided', () => {
|
||||
|
@ -131,7 +131,7 @@ describe('BackgroundSessionService', () => {
|
|||
const response = await service.get(sessionId, { savedObjectsClient });
|
||||
|
||||
expect(response).toBe(mockSavedObject);
|
||||
expect(savedObjectsClient.get).toHaveBeenCalledWith(BACKGROUND_SESSION_TYPE, sessionId);
|
||||
expect(savedObjectsClient.get).toHaveBeenCalledWith(SEARCH_SESSION_TYPE, sessionId);
|
||||
});
|
||||
|
||||
it('find calls saved objects client', async () => {
|
||||
|
@ -153,7 +153,7 @@ describe('BackgroundSessionService', () => {
|
|||
expect(response).toBe(mockResponse);
|
||||
expect(savedObjectsClient.find).toHaveBeenCalledWith({
|
||||
...options,
|
||||
type: BACKGROUND_SESSION_TYPE,
|
||||
type: SEARCH_SESSION_TYPE,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -169,7 +169,7 @@ describe('BackgroundSessionService', () => {
|
|||
|
||||
expect(response).toBe(mockUpdateSavedObject);
|
||||
expect(savedObjectsClient.update).toHaveBeenCalledWith(
|
||||
BACKGROUND_SESSION_TYPE,
|
||||
SEARCH_SESSION_TYPE,
|
||||
sessionId,
|
||||
attributes
|
||||
);
|
||||
|
@ -181,14 +181,14 @@ describe('BackgroundSessionService', () => {
|
|||
const response = await service.delete(sessionId, { savedObjectsClient });
|
||||
|
||||
expect(response).toEqual({});
|
||||
expect(savedObjectsClient.delete).toHaveBeenCalledWith(BACKGROUND_SESSION_TYPE, sessionId);
|
||||
expect(savedObjectsClient.delete).toHaveBeenCalledWith(SEARCH_SESSION_TYPE, sessionId);
|
||||
});
|
||||
|
||||
describe('search', () => {
|
||||
const mockSearch = jest.fn().mockReturnValue(of({}));
|
||||
const mockStrategy = { search: mockSearch };
|
||||
const mockSearchDeps = {} as SearchStrategyDependencies;
|
||||
const mockDeps = {} as BackgroundSessionDependencies;
|
||||
const mockDeps = {} as SearchSessionDependencies;
|
||||
|
||||
beforeEach(() => {
|
||||
mockSearch.mockClear();
|
||||
|
@ -300,14 +300,14 @@ describe('BackgroundSessionService', () => {
|
|||
);
|
||||
|
||||
expect(savedObjectsClient.create).toHaveBeenCalledWith(
|
||||
BACKGROUND_SESSION_TYPE,
|
||||
SEARCH_SESSION_TYPE,
|
||||
{
|
||||
name,
|
||||
created,
|
||||
expires,
|
||||
initialState: {},
|
||||
restoreState: {},
|
||||
status: BackgroundSessionStatus.IN_PROGRESS,
|
||||
status: SearchSessionStatus.IN_PROGRESS,
|
||||
idMapping: {},
|
||||
appId,
|
||||
urlGeneratorId,
|
||||
|
@ -335,7 +335,7 @@ describe('BackgroundSessionService', () => {
|
|||
{ savedObjectsClient }
|
||||
);
|
||||
|
||||
expect(savedObjectsClient.update).toHaveBeenCalledWith(BACKGROUND_SESSION_TYPE, sessionId, {
|
||||
expect(savedObjectsClient.update).toHaveBeenCalledWith(SEARCH_SESSION_TYPE, sessionId, {
|
||||
idMapping: {
|
||||
[requestHash]: {
|
||||
id: searchId,
|
||||
|
@ -385,7 +385,7 @@ describe('BackgroundSessionService', () => {
|
|||
const searchId = 'FnpFYlBpeXdCUTMyZXhCLTc1TWFKX0EbdDFDTzJzTE1Sck9PVTBIcW1iU05CZzo4MDA0';
|
||||
const mockSession = {
|
||||
id: 'd7170a35-7e2c-48d6-8dec-9a056721b489',
|
||||
type: BACKGROUND_SESSION_TYPE,
|
||||
type: SEARCH_SESSION_TYPE,
|
||||
attributes: {
|
||||
name: 'my_name',
|
||||
appId: 'my_app_id',
|
||||
|
|
|
@ -30,12 +30,12 @@ import {
|
|||
SearchStrategyDependencies,
|
||||
} from '../../../../../../src/plugins/data/server';
|
||||
import {
|
||||
BackgroundSessionSavedObjectAttributes,
|
||||
BackgroundSessionFindOptions,
|
||||
BackgroundSessionSearchInfo,
|
||||
BackgroundSessionStatus,
|
||||
SearchSessionSavedObjectAttributes,
|
||||
SearchSessionFindOptions,
|
||||
SearchSessionRequestInfo,
|
||||
SearchSessionStatus,
|
||||
} from '../../../common';
|
||||
import { BACKGROUND_SESSION_TYPE } from '../../saved_objects';
|
||||
import { SEARCH_SESSION_TYPE } from '../../saved_objects';
|
||||
import { createRequestHash } from './utils';
|
||||
import { ConfigSchema } from '../../../config';
|
||||
|
||||
|
@ -45,17 +45,17 @@ export const INMEM_TRACKING_INTERVAL = 10 * 1000;
|
|||
export const INMEM_TRACKING_TIMEOUT_SEC = 60;
|
||||
export const MAX_UPDATE_RETRIES = 3;
|
||||
|
||||
export interface BackgroundSessionDependencies {
|
||||
export interface SearchSessionDependencies {
|
||||
savedObjectsClient: SavedObjectsClientContract;
|
||||
}
|
||||
|
||||
export interface SessionInfo {
|
||||
insertTime: Moment;
|
||||
retryCount: number;
|
||||
ids: Map<string, BackgroundSessionSearchInfo>;
|
||||
ids: Map<string, SearchSessionRequestInfo>;
|
||||
}
|
||||
|
||||
export class BackgroundSessionService implements ISessionService {
|
||||
export class SearchSessionService implements ISessionService {
|
||||
/**
|
||||
* Map of sessionId to { [requestHash]: searchId }
|
||||
* @private
|
||||
|
@ -79,7 +79,7 @@ export class BackgroundSessionService implements ISessionService {
|
|||
const config = await config$.pipe(first()).toPromise();
|
||||
if (config.search.sendToBackground.enabled) {
|
||||
this.logger.debug(`setupMonitoring | Enabling monitoring`);
|
||||
const internalRepo = core.savedObjects.createInternalRepository([BACKGROUND_SESSION_TYPE]);
|
||||
const internalRepo = core.savedObjects.createInternalRepository([SEARCH_SESSION_TYPE]);
|
||||
this.internalSavedObjectsClient = new SavedObjectsClient(internalRepo);
|
||||
this.monitorMappedIds();
|
||||
}
|
||||
|
@ -92,7 +92,7 @@ export class BackgroundSessionService implements ISessionService {
|
|||
private sessionIdsAsFilters(sessionIds: string[]): KueryNode {
|
||||
return nodeBuilder.or(
|
||||
sessionIds.map((id) => {
|
||||
return nodeBuilder.is(`${BACKGROUND_SESSION_TYPE}.attributes.sessionId`, id);
|
||||
return nodeBuilder.is(`${SEARCH_SESSION_TYPE}.attributes.sessionId`, id);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
@ -107,9 +107,9 @@ export class BackgroundSessionService implements ISessionService {
|
|||
*/
|
||||
private async getAllMappedSavedObjects() {
|
||||
const filter = this.sessionIdsAsFilters(Array.from(this.sessionSearchMap.keys()));
|
||||
const res = await this.internalSavedObjectsClient.find<BackgroundSessionSavedObjectAttributes>({
|
||||
const res = await this.internalSavedObjectsClient.find<SearchSessionSavedObjectAttributes>({
|
||||
perPage: INMEM_MAX_SESSIONS, // If there are more sessions in memory, they will be synced when some items are cleared out.
|
||||
type: BACKGROUND_SESSION_TYPE,
|
||||
type: SEARCH_SESSION_TYPE,
|
||||
filter,
|
||||
namespaces: ['*'],
|
||||
});
|
||||
|
@ -175,13 +175,13 @@ export class BackgroundSessionService implements ISessionService {
|
|||
}
|
||||
|
||||
private async updateAllSavedObjects(
|
||||
activeMappingObjects: Array<SavedObject<BackgroundSessionSavedObjectAttributes>>
|
||||
activeMappingObjects: Array<SavedObject<SearchSessionSavedObjectAttributes>>
|
||||
) {
|
||||
if (!activeMappingObjects.length) return [];
|
||||
|
||||
this.logger.debug(`updateAllSavedObjects | Updating ${activeMappingObjects.length} items`);
|
||||
const updatedSessions: Array<
|
||||
SavedObjectsBulkUpdateObject<BackgroundSessionSavedObjectAttributes>
|
||||
SavedObjectsBulkUpdateObject<SearchSessionSavedObjectAttributes>
|
||||
> = activeMappingObjects
|
||||
.filter((so) => !so.error)
|
||||
.map((sessionSavedObject) => {
|
||||
|
@ -197,7 +197,7 @@ export class BackgroundSessionService implements ISessionService {
|
|||
};
|
||||
});
|
||||
|
||||
const updateResults = await this.internalSavedObjectsClient.bulkUpdate<BackgroundSessionSavedObjectAttributes>(
|
||||
const updateResults = await this.internalSavedObjectsClient.bulkUpdate<SearchSessionSavedObjectAttributes>(
|
||||
updatedSessions
|
||||
);
|
||||
return updateResults.saved_objects;
|
||||
|
@ -208,7 +208,7 @@ export class BackgroundSessionService implements ISessionService {
|
|||
searchRequest: Request,
|
||||
options: ISearchOptions,
|
||||
searchDeps: SearchStrategyDependencies,
|
||||
deps: BackgroundSessionDependencies
|
||||
deps: SearchSessionDependencies
|
||||
): Observable<Response> {
|
||||
// If this is a restored background search session, look up the ID using the provided sessionId
|
||||
const getSearchRequest = async () =>
|
||||
|
@ -236,12 +236,12 @@ export class BackgroundSessionService implements ISessionService {
|
|||
appId,
|
||||
created = new Date().toISOString(),
|
||||
expires = new Date(Date.now() + DEFAULT_EXPIRATION).toISOString(),
|
||||
status = BackgroundSessionStatus.IN_PROGRESS,
|
||||
status = SearchSessionStatus.IN_PROGRESS,
|
||||
urlGeneratorId,
|
||||
initialState = {},
|
||||
restoreState = {},
|
||||
}: Partial<BackgroundSessionSavedObjectAttributes>,
|
||||
{ savedObjectsClient }: BackgroundSessionDependencies
|
||||
}: Partial<SearchSessionSavedObjectAttributes>,
|
||||
{ savedObjectsClient }: SearchSessionDependencies
|
||||
) => {
|
||||
if (!name) throw new Error('Name is required');
|
||||
if (!appId) throw new Error('AppId is required');
|
||||
|
@ -261,8 +261,8 @@ export class BackgroundSessionService implements ISessionService {
|
|||
appId,
|
||||
sessionId,
|
||||
};
|
||||
const session = await savedObjectsClient.create<BackgroundSessionSavedObjectAttributes>(
|
||||
BACKGROUND_SESSION_TYPE,
|
||||
const session = await savedObjectsClient.create<SearchSessionSavedObjectAttributes>(
|
||||
SEARCH_SESSION_TYPE,
|
||||
attributes,
|
||||
{ id: sessionId }
|
||||
);
|
||||
|
@ -271,42 +271,42 @@ export class BackgroundSessionService implements ISessionService {
|
|||
};
|
||||
|
||||
// TODO: Throw an error if this session doesn't belong to this user
|
||||
public get = (sessionId: string, { savedObjectsClient }: BackgroundSessionDependencies) => {
|
||||
public get = (sessionId: string, { savedObjectsClient }: SearchSessionDependencies) => {
|
||||
this.logger.debug(`get | ${sessionId}`);
|
||||
return savedObjectsClient.get<BackgroundSessionSavedObjectAttributes>(
|
||||
BACKGROUND_SESSION_TYPE,
|
||||
return savedObjectsClient.get<SearchSessionSavedObjectAttributes>(
|
||||
SEARCH_SESSION_TYPE,
|
||||
sessionId
|
||||
);
|
||||
};
|
||||
|
||||
// TODO: Throw an error if this session doesn't belong to this user
|
||||
public find = (
|
||||
options: BackgroundSessionFindOptions,
|
||||
{ savedObjectsClient }: BackgroundSessionDependencies
|
||||
options: SearchSessionFindOptions,
|
||||
{ savedObjectsClient }: SearchSessionDependencies
|
||||
) => {
|
||||
return savedObjectsClient.find<BackgroundSessionSavedObjectAttributes>({
|
||||
return savedObjectsClient.find<SearchSessionSavedObjectAttributes>({
|
||||
...options,
|
||||
type: BACKGROUND_SESSION_TYPE,
|
||||
type: SEARCH_SESSION_TYPE,
|
||||
});
|
||||
};
|
||||
|
||||
// TODO: Throw an error if this session doesn't belong to this user
|
||||
public update = (
|
||||
sessionId: string,
|
||||
attributes: Partial<BackgroundSessionSavedObjectAttributes>,
|
||||
{ savedObjectsClient }: BackgroundSessionDependencies
|
||||
attributes: Partial<SearchSessionSavedObjectAttributes>,
|
||||
{ savedObjectsClient }: SearchSessionDependencies
|
||||
) => {
|
||||
this.logger.debug(`update | ${sessionId}`);
|
||||
return savedObjectsClient.update<BackgroundSessionSavedObjectAttributes>(
|
||||
BACKGROUND_SESSION_TYPE,
|
||||
return savedObjectsClient.update<SearchSessionSavedObjectAttributes>(
|
||||
SEARCH_SESSION_TYPE,
|
||||
sessionId,
|
||||
attributes
|
||||
);
|
||||
};
|
||||
|
||||
// TODO: Throw an error if this session doesn't belong to this user
|
||||
public delete = (sessionId: string, { savedObjectsClient }: BackgroundSessionDependencies) => {
|
||||
return savedObjectsClient.delete(BACKGROUND_SESSION_TYPE, sessionId);
|
||||
public delete = (sessionId: string, { savedObjectsClient }: SearchSessionDependencies) => {
|
||||
return savedObjectsClient.delete(SEARCH_SESSION_TYPE, sessionId);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -318,7 +318,7 @@ export class BackgroundSessionService implements ISessionService {
|
|||
searchRequest: IKibanaSearchRequest,
|
||||
searchId: string,
|
||||
{ sessionId, isStored, strategy }: ISearchOptions,
|
||||
deps: BackgroundSessionDependencies
|
||||
deps: SearchSessionDependencies
|
||||
) => {
|
||||
if (!sessionId || !searchId) return;
|
||||
this.logger.debug(`trackId | ${sessionId} | ${searchId}`);
|
||||
|
@ -339,7 +339,7 @@ export class BackgroundSessionService implements ISessionService {
|
|||
const map = this.sessionSearchMap.get(sessionId) ?? {
|
||||
insertTime: moment(),
|
||||
retryCount: 0,
|
||||
ids: new Map<string, BackgroundSessionSearchInfo>(),
|
||||
ids: new Map<string, SearchSessionRequestInfo>(),
|
||||
};
|
||||
map.ids.set(requestHash, searchInfo);
|
||||
this.sessionSearchMap.set(sessionId, map);
|
||||
|
@ -354,7 +354,7 @@ export class BackgroundSessionService implements ISessionService {
|
|||
public getId = async (
|
||||
searchRequest: IKibanaSearchRequest,
|
||||
{ sessionId, isStored, isRestore }: ISearchOptions,
|
||||
deps: BackgroundSessionDependencies
|
||||
deps: SearchSessionDependencies
|
||||
) => {
|
||||
if (!sessionId) {
|
||||
throw new Error('Session ID is required');
|
||||
|
@ -376,7 +376,7 @@ export class BackgroundSessionService implements ISessionService {
|
|||
public asScopedProvider = ({ savedObjects }: CoreStart) => {
|
||||
return (request: KibanaRequest) => {
|
||||
const savedObjectsClient = savedObjects.getScopedClient(request, {
|
||||
includedHiddenTypes: [BACKGROUND_SESSION_TYPE],
|
||||
includedHiddenTypes: [SEARCH_SESSION_TYPE],
|
||||
});
|
||||
const deps = { savedObjectsClient };
|
||||
return {
|
||||
|
@ -384,11 +384,11 @@ export class BackgroundSessionService implements ISessionService {
|
|||
strategy: ISearchStrategy<Request, Response>,
|
||||
...args: Parameters<ISearchStrategy<Request, Response>['search']>
|
||||
) => this.search(strategy, ...args, deps),
|
||||
save: (sessionId: string, attributes: Partial<BackgroundSessionSavedObjectAttributes>) =>
|
||||
save: (sessionId: string, attributes: Partial<SearchSessionSavedObjectAttributes>) =>
|
||||
this.save(sessionId, attributes, deps),
|
||||
get: (sessionId: string) => this.get(sessionId, deps),
|
||||
find: (options: BackgroundSessionFindOptions) => this.find(options, deps),
|
||||
update: (sessionId: string, attributes: Partial<BackgroundSessionSavedObjectAttributes>) =>
|
||||
find: (options: SearchSessionFindOptions) => this.find(options, deps),
|
||||
update: (sessionId: string, attributes: Partial<SearchSessionSavedObjectAttributes>) =>
|
||||
this.update(sessionId, attributes, deps),
|
||||
delete: (sessionId: string) => this.delete(sessionId, deps),
|
||||
};
|
||||
|
|
|
@ -73,7 +73,7 @@ Array [
|
|||
"dashboard",
|
||||
"query",
|
||||
"url",
|
||||
"background-session",
|
||||
"search-session",
|
||||
],
|
||||
"read": Array [
|
||||
"index-pattern",
|
||||
|
@ -207,7 +207,7 @@ Array [
|
|||
"query",
|
||||
"index-pattern",
|
||||
"url",
|
||||
"background-session",
|
||||
"search-session",
|
||||
],
|
||||
"read": Array [],
|
||||
},
|
||||
|
@ -559,7 +559,7 @@ Array [
|
|||
"dashboard",
|
||||
"query",
|
||||
"url",
|
||||
"background-session",
|
||||
"search-session",
|
||||
],
|
||||
"read": Array [
|
||||
"index-pattern",
|
||||
|
@ -693,7 +693,7 @@ Array [
|
|||
"query",
|
||||
"index-pattern",
|
||||
"url",
|
||||
"background-session",
|
||||
"search-session",
|
||||
],
|
||||
"read": Array [],
|
||||
},
|
||||
|
|
|
@ -89,7 +89,7 @@ export const buildOSSFeatures = ({ savedObjectTypes, includeTimelion }: BuildOSS
|
|||
),
|
||||
includeIn: 'all',
|
||||
savedObject: {
|
||||
all: ['background-session'],
|
||||
all: ['search-session'],
|
||||
read: [],
|
||||
},
|
||||
ui: ['storeSearchSession'],
|
||||
|
@ -254,7 +254,7 @@ export const buildOSSFeatures = ({ savedObjectTypes, includeTimelion }: BuildOSS
|
|||
),
|
||||
includeIn: 'all',
|
||||
savedObject: {
|
||||
all: ['background-session'],
|
||||
all: ['search-session'],
|
||||
read: [],
|
||||
},
|
||||
ui: ['storeSearchSession'],
|
||||
|
|
|
@ -7138,30 +7138,6 @@
|
|||
"xpack.dashboardMode.uiSettings.dashboardsOnlyRolesTitle": "ダッシュボード専用ロール",
|
||||
"xpack.data.advancedSettings.searchTimeout": "検索タイムアウト",
|
||||
"xpack.data.advancedSettings.searchTimeoutDesc": "検索セッションの最大タイムアウトを変更するか、0 に設定してタイムアウトを無効にすると、クエリは完了するまで実行されます。",
|
||||
"xpack.data.backgroundSessionIndicator.cancelButtonText": "キャンセル",
|
||||
"xpack.data.backgroundSessionIndicator.canceledIconAriaLabel": "キャンセル",
|
||||
"xpack.data.backgroundSessionIndicator.canceledText": "検索がキャンセルされました",
|
||||
"xpack.data.backgroundSessionIndicator.canceledTooltipText": "検索がキャンセルされました",
|
||||
"xpack.data.backgroundSessionIndicator.continueInBackgroundButtonText": "バックグラウンドで続行",
|
||||
"xpack.data.backgroundSessionIndicator.disabledDueToAutoRefreshMessage": "自動更新が有効な場合は、バックグラウンドに送信できません。",
|
||||
"xpack.data.backgroundSessionIndicator.loadingInTheBackgroundIconAriaLabel": "バックグラウンドで結果を読み込んでいます",
|
||||
"xpack.data.backgroundSessionIndicator.loadingInTheBackgroundIconTooltipText": "バックグラウンドで結果を読み込んでいます",
|
||||
"xpack.data.backgroundSessionIndicator.loadingInTheBackgroundText": "バックグラウンドで読み込んでいます",
|
||||
"xpack.data.backgroundSessionIndicator.loadingResultsIconAriaLabel": "結果を読み込み中",
|
||||
"xpack.data.backgroundSessionIndicator.loadingResultsIconTooltipText": "結果を読み込み中",
|
||||
"xpack.data.backgroundSessionIndicator.loadingResultsText": "読み込み中",
|
||||
"xpack.data.backgroundSessionIndicator.refreshButtonText": "更新",
|
||||
"xpack.data.backgroundSessionIndicator.restoredResultsIconAriaLabel": "結果が最新ではありません",
|
||||
"xpack.data.backgroundSessionIndicator.restoredResultsTooltipText": "結果が最新ではありません",
|
||||
"xpack.data.backgroundSessionIndicator.restoredText": "結果が最新ではありません",
|
||||
"xpack.data.backgroundSessionIndicator.resultLoadedInTheBackgroundIconAraText": "結果がバックグラウンドで読み込まれました",
|
||||
"xpack.data.backgroundSessionIndicator.resultLoadedInTheBackgroundIconTooltipText": "結果がバックグラウンドで読み込まれました",
|
||||
"xpack.data.backgroundSessionIndicator.resultLoadedInTheBackgroundText": "結果が読み込まれました",
|
||||
"xpack.data.backgroundSessionIndicator.resultsLoadedIconAriaLabel": "結果が読み込まれました",
|
||||
"xpack.data.backgroundSessionIndicator.resultsLoadedIconTooltipText": "結果が読み込まれました",
|
||||
"xpack.data.backgroundSessionIndicator.resultsLoadedText": "結果が読み込まれました",
|
||||
"xpack.data.backgroundSessionIndicator.saveButtonText": "保存",
|
||||
"xpack.data.backgroundSessionIndicator.viewBackgroundSessionsLinkText": "バックグラウンドセッションを表示",
|
||||
"xpack.data.kueryAutocomplete.andOperatorDescription": "{bothArguments} が true であることを条件とする",
|
||||
"xpack.data.kueryAutocomplete.andOperatorDescription.bothArgumentsText": "両方の引数",
|
||||
"xpack.data.kueryAutocomplete.equalOperatorDescription": "一部の値に{equals}",
|
||||
|
|
|
@ -7157,30 +7157,6 @@
|
|||
"xpack.dashboardMode.uiSettings.dashboardsOnlyRolesTitle": "仅限仪表板的角色",
|
||||
"xpack.data.advancedSettings.searchTimeout": "搜索超时",
|
||||
"xpack.data.advancedSettings.searchTimeoutDesc": "更改搜索会话的最大超时值,或设置为 0 以禁用超时,让查询运行至结束。",
|
||||
"xpack.data.backgroundSessionIndicator.cancelButtonText": "取消",
|
||||
"xpack.data.backgroundSessionIndicator.canceledIconAriaLabel": "已取消",
|
||||
"xpack.data.backgroundSessionIndicator.canceledText": "搜索已取消",
|
||||
"xpack.data.backgroundSessionIndicator.canceledTooltipText": "搜索已取消",
|
||||
"xpack.data.backgroundSessionIndicator.continueInBackgroundButtonText": "在后台继续",
|
||||
"xpack.data.backgroundSessionIndicator.disabledDueToAutoRefreshMessage": "启用自动刷新后,“发送到后台”不可用。",
|
||||
"xpack.data.backgroundSessionIndicator.loadingInTheBackgroundIconAriaLabel": "正在后台加载结果",
|
||||
"xpack.data.backgroundSessionIndicator.loadingInTheBackgroundIconTooltipText": "正在后台加载结果",
|
||||
"xpack.data.backgroundSessionIndicator.loadingInTheBackgroundText": "正在后台加载",
|
||||
"xpack.data.backgroundSessionIndicator.loadingResultsIconAriaLabel": "正在加载结果",
|
||||
"xpack.data.backgroundSessionIndicator.loadingResultsIconTooltipText": "正在加载结果",
|
||||
"xpack.data.backgroundSessionIndicator.loadingResultsText": "正在加载",
|
||||
"xpack.data.backgroundSessionIndicator.refreshButtonText": "刷新",
|
||||
"xpack.data.backgroundSessionIndicator.restoredResultsIconAriaLabel": "结果不再是最新",
|
||||
"xpack.data.backgroundSessionIndicator.restoredResultsTooltipText": "结果不再是最新",
|
||||
"xpack.data.backgroundSessionIndicator.restoredText": "结果不再是最新",
|
||||
"xpack.data.backgroundSessionIndicator.resultLoadedInTheBackgroundIconAraText": "结果已后台加载",
|
||||
"xpack.data.backgroundSessionIndicator.resultLoadedInTheBackgroundIconTooltipText": "结果已后台加载",
|
||||
"xpack.data.backgroundSessionIndicator.resultLoadedInTheBackgroundText": "结果已加载",
|
||||
"xpack.data.backgroundSessionIndicator.resultsLoadedIconAriaLabel": "结果已加载",
|
||||
"xpack.data.backgroundSessionIndicator.resultsLoadedIconTooltipText": "结果已加载",
|
||||
"xpack.data.backgroundSessionIndicator.resultsLoadedText": "结果已加载",
|
||||
"xpack.data.backgroundSessionIndicator.saveButtonText": "保存",
|
||||
"xpack.data.backgroundSessionIndicator.viewBackgroundSessionsLinkText": "查看后台会话",
|
||||
"xpack.data.kueryAutocomplete.andOperatorDescription": "需要{bothArguments}为 true",
|
||||
"xpack.data.kueryAutocomplete.andOperatorDescription.bothArgumentsText": "两个参数都",
|
||||
"xpack.data.kueryAutocomplete.equalOperatorDescription": "{equals}某一值",
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
"application_usage_daily": "43b8830d5d0df85a6823d290885fc9fd",
|
||||
"application_usage_totals": "3d1b76c39bfb2cc8296b024d73854724",
|
||||
"application_usage_transactional": "3d1b76c39bfb2cc8296b024d73854724",
|
||||
"background-session": "404e2e2355a045f400c393e751445b42",
|
||||
"search-session": "404e2e2355a045f400c393e751445b42",
|
||||
"canvas-element": "7390014e1091044523666d97247392fc",
|
||||
"canvas-workpad": "b0a1706d356228dbdcb4a17e6b9eb231",
|
||||
"canvas-workpad-template": "ae2673f678281e2c055d764b153e9715",
|
||||
|
@ -297,7 +297,7 @@
|
|||
"dynamic": "false",
|
||||
"type": "object"
|
||||
},
|
||||
"background-session": {
|
||||
"search-session": {
|
||||
"properties": {
|
||||
"appId": {
|
||||
"type": "keyword"
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
"application_usage_daily": "43b8830d5d0df85a6823d290885fc9fd",
|
||||
"application_usage_totals": "3d1b76c39bfb2cc8296b024d73854724",
|
||||
"application_usage_transactional": "3d1b76c39bfb2cc8296b024d73854724",
|
||||
"background-session": "721df406dbb7e35ac22e4df6c3ad2b2a",
|
||||
"search-session": "721df406dbb7e35ac22e4df6c3ad2b2a",
|
||||
"canvas-element": "7390014e1091044523666d97247392fc",
|
||||
"canvas-workpad": "b0a1706d356228dbdcb4a17e6b9eb231",
|
||||
"canvas-workpad-template": "ae2673f678281e2c055d764b153e9715",
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
import { FtrProviderContext } from '../ftr_provider_context';
|
||||
import { WebElementWrapper } from '../../../../test/functional/services/lib/web_element_wrapper';
|
||||
|
||||
const SEND_TO_BACKGROUND_TEST_SUBJ = 'backgroundSessionIndicator';
|
||||
const SEND_TO_BACKGROUND_POPOVER_CONTENT_TEST_SUBJ = 'backgroundSessionIndicatorPopoverContainer';
|
||||
const SEND_TO_BACKGROUND_TEST_SUBJ = 'searchSessionIndicator';
|
||||
const SEND_TO_BACKGROUND_POPOVER_CONTENT_TEST_SUBJ = 'searchSessionIndicatorPopoverContainer';
|
||||
|
||||
type SessionStateType =
|
||||
| 'none'
|
||||
|
@ -42,26 +42,26 @@ export function SendToBackgroundProvider({ getService }: FtrProviderContext) {
|
|||
});
|
||||
}
|
||||
|
||||
public async viewBackgroundSessions() {
|
||||
public async viewSearchSessions() {
|
||||
await this.ensurePopoverOpened();
|
||||
await testSubjects.click('backgroundSessionIndicatorViewBackgroundSessionsLink');
|
||||
await testSubjects.click('searchSessionIndicatorviewSearchSessionsLink');
|
||||
}
|
||||
|
||||
public async save() {
|
||||
await this.ensurePopoverOpened();
|
||||
await testSubjects.click('backgroundSessionIndicatorSaveBtn');
|
||||
await testSubjects.click('searchSessionIndicatorSaveBtn');
|
||||
await this.ensurePopoverClosed();
|
||||
}
|
||||
|
||||
public async cancel() {
|
||||
await this.ensurePopoverOpened();
|
||||
await testSubjects.click('backgroundSessionIndicatorCancelBtn');
|
||||
await testSubjects.click('searchSessionIndicatorCancelBtn');
|
||||
await this.ensurePopoverClosed();
|
||||
}
|
||||
|
||||
public async refresh() {
|
||||
await this.ensurePopoverOpened();
|
||||
await testSubjects.click('backgroundSessionIndicatorRefreshBtn');
|
||||
await testSubjects.click('searchSessionIndicatorRefreshBtn');
|
||||
await this.ensurePopoverClosed();
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue