-
Notifications
You must be signed in to change notification settings - Fork 8.3k
/
Copy pathload_saved_search.ts
179 lines (163 loc) · 6.77 KB
/
load_saved_search.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import type { SavedSearch } from '@kbn/saved-search-plugin/public';
import { cloneDeep, isEqual } from 'lodash';
import { getDataViewByTextBasedQueryLang } from '../utils/get_data_view_by_text_based_query_lang';
import { isTextBasedQuery } from '../utils/is_text_based_query';
import { loadAndResolveDataView } from '../utils/resolve_data_view';
import { DiscoverInternalStateContainer } from './discover_internal_state_container';
import { DiscoverDataStateContainer } from './discover_data_state_container';
import { cleanupUrlState } from '../utils/cleanup_url_state';
import { getValidFilters } from '../../../utils/get_valid_filters';
import { DiscoverStateContainer, LoadParams } from './discover_state';
import { addLog } from '../../../utils/add_log';
import { DiscoverSavedSearchContainer } from './discover_saved_search_container';
import {
DiscoverAppState,
DiscoverAppStateContainer,
getInitialState,
} from './discover_app_state_container';
import { DiscoverServices } from '../../../build_services';
interface LoadSavedSearchDeps {
appStateContainer: DiscoverAppStateContainer;
dataStateContainer: DiscoverDataStateContainer;
internalStateContainer: DiscoverInternalStateContainer;
savedSearchContainer: DiscoverSavedSearchContainer;
services: DiscoverServices;
setDataView: DiscoverStateContainer['actions']['setDataView'];
}
/**
* Loading persisted saved searches or existing ones and updating services accordingly
* @param params
* @param deps
*/
export const loadSavedSearch = async (
params: LoadParams,
deps: LoadSavedSearchDeps
): Promise<SavedSearch> => {
addLog('[discoverState] loadSavedSearch');
const { savedSearchId } = params ?? {};
const { appStateContainer, internalStateContainer, savedSearchContainer, services } = deps;
const appStateExists = !appStateContainer.isEmptyURL();
const appState = appStateExists ? appStateContainer.getState() : undefined;
// Loading the saved search or creating a new one
let nextSavedSearch = savedSearchId
? await savedSearchContainer.load(savedSearchId)
: await savedSearchContainer.new(
await getStateDataView(params, { services, appState, internalStateContainer })
);
// Cleaning up the previous state
services.filterManager.setAppFilters([]);
services.data.query.queryString.clearQuery();
// reset appState in case a saved search with id is loaded and
// the url is empty so the saved search is loaded in a clean
// state else it might be updated by the previous app state
if (!appStateExists) {
appStateContainer.set({});
}
// Update saved search by a given app state (in URL)
if (appState) {
if (savedSearchId && appState.index) {
// This is for the case appState is overwriting the loaded saved search data view
const savedSearchDataViewId = nextSavedSearch.searchSource.getField('index')?.id;
const stateDataView = await getStateDataView(params, {
services,
appState,
internalStateContainer,
savedSearch: nextSavedSearch,
});
const dataViewDifferentToAppState = stateDataView.id !== savedSearchDataViewId;
if (stateDataView && (dataViewDifferentToAppState || !savedSearchDataViewId)) {
nextSavedSearch.searchSource.setField('index', stateDataView);
}
}
nextSavedSearch = savedSearchContainer.update({
nextDataView: nextSavedSearch.searchSource.getField('index'),
nextState: appState,
});
}
// Update app state container with the next state derived from the next saved search
const nextAppState = getInitialState(undefined, nextSavedSearch, services);
const mergedAppState = appState
? { ...nextAppState, ...cleanupUrlState({ ...appState }, services.uiSettings) }
: nextAppState;
appStateContainer.resetToState(mergedAppState);
// Update all other services and state containers by the next saved search
updateBySavedSearch(nextSavedSearch, deps);
return nextSavedSearch;
};
/**
* Update services and state containers based on the given saved search
* @param savedSearch
* @param deps
*/
function updateBySavedSearch(savedSearch: SavedSearch, deps: LoadSavedSearchDeps) {
const { dataStateContainer, internalStateContainer, services, setDataView } = deps;
const savedSearchDataView = savedSearch.searchSource.getField('index')!;
setDataView(savedSearchDataView);
if (!savedSearchDataView.isPersisted()) {
internalStateContainer.transitions.appendAdHocDataViews(savedSearchDataView);
}
// Finally notify dataStateContainer, data.query and filterManager about new derived state
dataStateContainer.reset(savedSearch);
// set data service filters
const filters = savedSearch.searchSource.getField('filter');
if (Array.isArray(filters) && filters.length) {
services.data.query.filterManager.setAppFilters(cloneDeep(filters));
}
// some filters may not be valid for this context, so update
// the filter manager with a modified list of valid filters
const currentFilters = services.filterManager.getFilters();
const validFilters = getValidFilters(savedSearchDataView, currentFilters);
if (!isEqual(currentFilters, validFilters)) {
services.filterManager.setFilters(validFilters);
}
// set data service query
const query = savedSearch.searchSource.getField('query');
if (query) {
services.data.query.queryString.setQuery(query);
}
}
// Get the data view to actually use. There are several conditions to consider which data view is used
// 1. If a data view is passed in, use that
// 2. If an appState with index set is passed in, use the data view from that
// 3. If a saved search is passed in, use the data view from that
// And provide a fallback data view if 2. or 3. don't exist, were deleted or invalid
const getStateDataView = async (
params: LoadParams,
{
savedSearch,
appState,
services,
internalStateContainer,
}: {
savedSearch?: SavedSearch;
appState?: DiscoverAppState;
services: DiscoverServices;
internalStateContainer: DiscoverInternalStateContainer;
}
) => {
const { dataView, dataViewSpec } = params ?? {};
if (dataView) {
return dataView;
}
const query = appState?.query;
if (isTextBasedQuery(query)) {
return await getDataViewByTextBasedQueryLang(query, dataView, services);
}
const result = await loadAndResolveDataView(
{
id: appState?.index,
dataViewSpec,
savedSearch,
isTextBasedQuery: isTextBasedQuery(appState?.query),
},
{ services, internalStateContainer }
);
return result.dataView;
};