* [Data] Query Input String manager (#72093) * improve test stability * query string input manager (needed for search demo) * docs * dashboard * Fix jest * mock fix * Allow restoring a saved query * sync url * Luke's fix to test * cleanup * lens jest tests * docs * use queryStringManager.getDefaultQuery Don't sync query to global state * Update app.test.tsx lens mock * jest fix * jest * use new api in the example * Rename state param to query to match url state * Apply changes to discover * Update src/plugins/data/public/query/query_string/index.ts Co-authored-by: Anton Dosov <dosantappdev@gmail.com> * Improve query string state manager * Cleanup dashboard code * Handle refresh button * Set initial dashboard state * visualize state * remove unused * docs * fix example * fix jest * fix filter app state in discover * fix maps test * jest Co-authored-by: Anton Dosov <anton.dosov@elastic.co> Co-authored-by: Anton Dosov <dosantappdev@gmail.com> Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com> # Conflicts: # src/plugins/data/public/public.api.md * docs
This commit is contained in:
parent
9ed82273d6
commit
25ceed986b
|
@ -9,9 +9,10 @@ Helper to setup two-way syncing of global data and a state container
|
|||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
connectToQueryState: <S extends QueryState>({ timefilter: { timefilter }, filterManager, state$, }: Pick<QueryStart | QuerySetup, 'timefilter' | 'filterManager' | 'state$'>, stateContainer: BaseStateContainer<S>, syncConfig: {
|
||||
connectToQueryState: <S extends QueryState>({ timefilter: { timefilter }, filterManager, queryString, state$, }: Pick<QueryStart | QuerySetup, 'timefilter' | 'filterManager' | 'queryString' | 'state$'>, stateContainer: BaseStateContainer<S>, syncConfig: {
|
||||
time?: boolean;
|
||||
refreshInterval?: boolean;
|
||||
filters?: FilterStateStore | boolean;
|
||||
query?: boolean;
|
||||
}) => () => void
|
||||
```
|
||||
|
|
|
@ -17,6 +17,7 @@ export interface QueryState
|
|||
| Property | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| [filters](./kibana-plugin-plugins-data-public.querystate.filters.md) | <code>Filter[]</code> | |
|
||||
| [query](./kibana-plugin-plugins-data-public.querystate.query.md) | <code>Query</code> | |
|
||||
| [refreshInterval](./kibana-plugin-plugins-data-public.querystate.refreshinterval.md) | <code>RefreshInterval</code> | |
|
||||
| [time](./kibana-plugin-plugins-data-public.querystate.time.md) | <code>TimeRange</code> | |
|
||||
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
<!-- 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) > [QueryState](./kibana-plugin-plugins-data-public.querystate.md) > [query](./kibana-plugin-plugins-data-public.querystate.query.md)
|
||||
|
||||
## QueryState.query property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
query?: Query;
|
||||
```
|
|
@ -9,7 +9,7 @@ Helper to setup syncing of global data with the URL
|
|||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
syncQueryStateWithUrl: (query: Pick<QueryStart | QuerySetup, 'filterManager' | 'timefilter' | 'state$'>, kbnUrlStateStorage: IKbnUrlStateStorage) => {
|
||||
syncQueryStateWithUrl: (query: Pick<QueryStart | QuerySetup, 'filterManager' | 'timefilter' | 'queryString' | 'state$'>, kbnUrlStateStorage: IKbnUrlStateStorage) => {
|
||||
stop: () => void;
|
||||
hasInheritedQueryFromUrl: boolean;
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import React, { useEffect, useRef, useState, useCallback } from 'react';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { History } from 'history';
|
||||
import { FormattedMessage, I18nProvider } from '@kbn/i18n/react';
|
||||
import { Router } from 'react-router-dom';
|
||||
|
@ -85,16 +85,9 @@ const App = ({ navigation, data, history, kbnUrlStateStorage }: StateDemoAppDeps
|
|||
useGlobalStateSyncing(data.query, kbnUrlStateStorage);
|
||||
useAppStateSyncing(appStateContainer, data.query, kbnUrlStateStorage);
|
||||
|
||||
const onQuerySubmit = useCallback(
|
||||
({ query }) => {
|
||||
appStateContainer.set({ ...appState, query });
|
||||
},
|
||||
[appStateContainer, appState]
|
||||
);
|
||||
|
||||
const indexPattern = useIndexPattern(data);
|
||||
if (!indexPattern)
|
||||
return <div>No index pattern found. Please create an intex patter before loading...</div>;
|
||||
return <div>No index pattern found. Please create an index patter before loading...</div>;
|
||||
|
||||
// Render the application DOM.
|
||||
// Note that `navigation.ui.TopNavMenu` is a stateful component exported on the `navigation` plugin's start contract.
|
||||
|
@ -107,8 +100,6 @@ const App = ({ navigation, data, history, kbnUrlStateStorage }: StateDemoAppDeps
|
|||
showSearchBar={true}
|
||||
indexPatterns={[indexPattern]}
|
||||
useDefaultBehaviors={true}
|
||||
onQuerySubmit={onQuerySubmit}
|
||||
query={appState.query}
|
||||
showSaveQuery={true}
|
||||
/>
|
||||
<EuiPage restrictWidth="1000px">
|
||||
|
@ -200,7 +191,7 @@ function useAppStateSyncing<AppState extends QueryState>(
|
|||
const stopSyncingQueryAppStateWithStateContainer = connectToQueryState(
|
||||
query,
|
||||
appStateContainer,
|
||||
{ filters: esFilters.FilterStateStore.APP_STATE }
|
||||
{ filters: esFilters.FilterStateStore.APP_STATE, query: true }
|
||||
);
|
||||
|
||||
// sets up syncing app state container with url
|
||||
|
|
|
@ -52,7 +52,10 @@ export interface DashboardAppScope extends ng.IScope {
|
|||
expandedPanel?: string;
|
||||
getShouldShowEditHelp: () => boolean;
|
||||
getShouldShowViewHelp: () => boolean;
|
||||
updateQueryAndFetch: ({ query, dateRange }: { query: Query; dateRange?: TimeRange }) => void;
|
||||
handleRefresh: (
|
||||
{ query, dateRange }: { query?: Query; dateRange: TimeRange },
|
||||
isUpdate?: boolean
|
||||
) => void;
|
||||
topNavMenu: any;
|
||||
showAddPanel: any;
|
||||
showSaveQuery: boolean;
|
||||
|
|
|
@ -25,12 +25,11 @@ import React, { useState, ReactElement } from 'react';
|
|||
import ReactDOM from 'react-dom';
|
||||
import angular from 'angular';
|
||||
|
||||
import { Observable, pipe, Subscription } from 'rxjs';
|
||||
import { filter, map, mapTo, startWith, switchMap } from 'rxjs/operators';
|
||||
import { Observable, pipe, Subscription, merge } from 'rxjs';
|
||||
import { filter, map, debounceTime, mapTo, startWith, switchMap } from 'rxjs/operators';
|
||||
import { History } from 'history';
|
||||
import { SavedObjectSaveOpts } from 'src/plugins/saved_objects/public';
|
||||
import { NavigationPublicPluginStart as NavigationStart } from 'src/plugins/navigation/public';
|
||||
import { TimeRange } from 'src/plugins/data/public';
|
||||
import { DashboardEmptyScreen, DashboardEmptyScreenProps } from './dashboard_empty_screen';
|
||||
|
||||
import {
|
||||
|
@ -38,11 +37,9 @@ import {
|
|||
esFilters,
|
||||
IndexPattern,
|
||||
IndexPatternsContract,
|
||||
Query,
|
||||
QueryState,
|
||||
SavedQuery,
|
||||
syncQueryStateWithUrl,
|
||||
UI_SETTINGS,
|
||||
} from '../../../data/public';
|
||||
import { getSavedObjectFinder, SaveResult, showSaveModal } from '../../../saved_objects/public';
|
||||
|
||||
|
@ -81,8 +78,8 @@ import {
|
|||
addFatalError,
|
||||
AngularHttpError,
|
||||
KibanaLegacyStart,
|
||||
migrateLegacyQuery,
|
||||
subscribeWithScope,
|
||||
migrateLegacyQuery,
|
||||
} from '../../../kibana_legacy/public';
|
||||
|
||||
export interface DashboardAppControllerDependencies extends RenderDeps {
|
||||
|
@ -127,7 +124,6 @@ export class DashboardAppController {
|
|||
$route,
|
||||
$routeParams,
|
||||
dashboardConfig,
|
||||
localStorage,
|
||||
indexPatterns,
|
||||
savedQueryService,
|
||||
embeddable,
|
||||
|
@ -153,8 +149,8 @@ export class DashboardAppController {
|
|||
navigation,
|
||||
}: DashboardAppControllerDependencies) {
|
||||
const filterManager = queryService.filterManager;
|
||||
const queryFilter = filterManager;
|
||||
const timefilter = queryService.timefilter.timefilter;
|
||||
const queryStringManager = queryService.queryString;
|
||||
const isEmbeddedExternally = Boolean($routeParams.embed);
|
||||
|
||||
// url param rules should only apply when embedded (e.g. url?embed=true)
|
||||
|
@ -188,20 +184,30 @@ export class DashboardAppController {
|
|||
// sync initial app filters from state to filterManager
|
||||
// if there is an existing similar global filter, then leave it as global
|
||||
filterManager.setAppFilters(_.cloneDeep(dashboardStateManager.appState.filters));
|
||||
queryStringManager.setQuery(migrateLegacyQuery(dashboardStateManager.appState.query));
|
||||
|
||||
// setup syncing of app filters between appState and filterManager
|
||||
const stopSyncingAppFilters = connectToQueryState(
|
||||
queryService,
|
||||
{
|
||||
set: ({ filters }) => dashboardStateManager.setFilters(filters || []),
|
||||
get: () => ({ filters: dashboardStateManager.appState.filters }),
|
||||
set: ({ filters, query }) => {
|
||||
dashboardStateManager.setFilters(filters || []);
|
||||
dashboardStateManager.setQuery(query || queryStringManager.getDefaultQuery());
|
||||
},
|
||||
get: () => ({
|
||||
filters: dashboardStateManager.appState.filters,
|
||||
query: dashboardStateManager.getQuery(),
|
||||
}),
|
||||
state$: dashboardStateManager.appState$.pipe(
|
||||
map((state) => ({
|
||||
filters: state.filters,
|
||||
query: queryStringManager.formatQuery(state.query),
|
||||
}))
|
||||
),
|
||||
},
|
||||
{
|
||||
filters: esFilters.FilterStateStore.APP_STATE,
|
||||
query: true,
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -331,7 +337,7 @@ export class DashboardAppController {
|
|||
const isEmptyInReadonlyMode = shouldShowUnauthorizedEmptyState();
|
||||
return {
|
||||
id: dashboardStateManager.savedDashboard.id || '',
|
||||
filters: queryFilter.getFilters(),
|
||||
filters: filterManager.getFilters(),
|
||||
hidePanelTitles: dashboardStateManager.getHidePanelTitles(),
|
||||
query: $scope.model.query,
|
||||
timeRange: {
|
||||
|
@ -356,7 +362,7 @@ export class DashboardAppController {
|
|||
// https://github.com/angular/angular.js/wiki/Understanding-Scopes
|
||||
$scope.model = {
|
||||
query: dashboardStateManager.getQuery(),
|
||||
filters: queryFilter.getFilters(),
|
||||
filters: filterManager.getFilters(),
|
||||
timeRestore: dashboardStateManager.getTimeRestore(),
|
||||
title: dashboardStateManager.getTitle(),
|
||||
description: dashboardStateManager.getDescription(),
|
||||
|
@ -420,12 +426,12 @@ export class DashboardAppController {
|
|||
if (
|
||||
!esFilters.compareFilters(
|
||||
container.getInput().filters,
|
||||
queryFilter.getFilters(),
|
||||
filterManager.getFilters(),
|
||||
esFilters.COMPARE_ALL_OPTIONS
|
||||
)
|
||||
) {
|
||||
// Add filters modifies the object passed to it, hence the clone deep.
|
||||
queryFilter.addFilters(_.cloneDeep(container.getInput().filters));
|
||||
filterManager.addFilters(_.cloneDeep(container.getInput().filters));
|
||||
|
||||
dashboardStateManager.applyFilters(
|
||||
$scope.model.query,
|
||||
|
@ -487,13 +493,8 @@ export class DashboardAppController {
|
|||
});
|
||||
|
||||
dashboardStateManager.applyFilters(
|
||||
dashboardStateManager.getQuery() || {
|
||||
query: '',
|
||||
language:
|
||||
localStorage.get('kibana.userQueryLanguage') ||
|
||||
uiSettings.get(UI_SETTINGS.SEARCH_QUERY_LANGUAGE),
|
||||
},
|
||||
queryFilter.getFilters()
|
||||
dashboardStateManager.getQuery() || queryStringManager.getDefaultQuery(),
|
||||
filterManager.getFilters()
|
||||
);
|
||||
|
||||
timefilter.disableTimeRangeSelector();
|
||||
|
@ -567,21 +568,13 @@ export class DashboardAppController {
|
|||
}
|
||||
};
|
||||
|
||||
$scope.updateQueryAndFetch = function ({ query, dateRange }) {
|
||||
if (dateRange) {
|
||||
timefilter.setTime(dateRange);
|
||||
}
|
||||
|
||||
const oldQuery = $scope.model.query;
|
||||
if (_.isEqual(oldQuery, query)) {
|
||||
$scope.handleRefresh = function (_payload, isUpdate) {
|
||||
if (isUpdate === false) {
|
||||
// The user can still request a reload in the query bar, even if the
|
||||
// query is the same, and in that case, we have to explicitly ask for
|
||||
// a reload, since no state changes will cause it.
|
||||
lastReloadRequestTime = new Date().getTime();
|
||||
refreshDashboardContainer();
|
||||
} else {
|
||||
$scope.model.query = query;
|
||||
dashboardStateManager.applyFilters($scope.model.query, $scope.model.filters);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -600,7 +593,7 @@ export class DashboardAppController {
|
|||
// Making this method sync broke the updates.
|
||||
// Temporary fix, until we fix the complex state in this file.
|
||||
setTimeout(() => {
|
||||
queryFilter.setFilters(allFilters);
|
||||
filterManager.setFilters(allFilters);
|
||||
}, 0);
|
||||
};
|
||||
|
||||
|
@ -633,11 +626,6 @@ export class DashboardAppController {
|
|||
|
||||
$scope.indexPatterns = [];
|
||||
|
||||
$scope.$watch('model.query', (newQuery: Query) => {
|
||||
const query = migrateLegacyQuery(newQuery) as Query;
|
||||
$scope.updateQueryAndFetch({ query });
|
||||
});
|
||||
|
||||
$scope.$watch(
|
||||
() => dashboardCapabilities.saveQuery,
|
||||
(newCapability) => {
|
||||
|
@ -678,18 +666,11 @@ export class DashboardAppController {
|
|||
showFilterBar,
|
||||
indexPatterns: $scope.indexPatterns,
|
||||
showSaveQuery: $scope.showSaveQuery,
|
||||
query: $scope.model.query,
|
||||
savedQuery: $scope.savedQuery,
|
||||
onSavedQueryIdChange,
|
||||
savedQueryId: dashboardStateManager.getSavedQueryId(),
|
||||
useDefaultBehaviors: true,
|
||||
onQuerySubmit: (payload: { dateRange: TimeRange; query?: Query }): void => {
|
||||
if (!payload.query) {
|
||||
$scope.updateQueryAndFetch({ query: $scope.model.query, dateRange: payload.dateRange });
|
||||
} else {
|
||||
$scope.updateQueryAndFetch({ query: payload.query, dateRange: payload.dateRange });
|
||||
}
|
||||
},
|
||||
onQuerySubmit: $scope.handleRefresh,
|
||||
};
|
||||
};
|
||||
const dashboardNavBar = document.getElementById('dashboardChrome');
|
||||
|
@ -704,25 +685,11 @@ export class DashboardAppController {
|
|||
};
|
||||
|
||||
$scope.timefilterSubscriptions$ = new Subscription();
|
||||
|
||||
const timeChanges$ = merge(timefilter.getRefreshIntervalUpdate$(), timefilter.getTimeUpdate$());
|
||||
$scope.timefilterSubscriptions$.add(
|
||||
subscribeWithScope(
|
||||
$scope,
|
||||
timefilter.getRefreshIntervalUpdate$(),
|
||||
{
|
||||
next: () => {
|
||||
updateState();
|
||||
refreshDashboardContainer();
|
||||
},
|
||||
},
|
||||
(error: AngularHttpError | Error | string) => addFatalError(fatalErrors, error)
|
||||
)
|
||||
);
|
||||
|
||||
$scope.timefilterSubscriptions$.add(
|
||||
subscribeWithScope(
|
||||
$scope,
|
||||
timefilter.getTimeUpdate$(),
|
||||
timeChanges$,
|
||||
{
|
||||
next: () => {
|
||||
updateState();
|
||||
|
@ -1095,13 +1062,21 @@ export class DashboardAppController {
|
|||
|
||||
updateViewMode(dashboardStateManager.getViewMode());
|
||||
|
||||
const filterChanges = merge(filterManager.getUpdates$(), queryStringManager.getUpdates$()).pipe(
|
||||
debounceTime(100)
|
||||
);
|
||||
|
||||
// update root source when filters update
|
||||
const updateSubscription = queryFilter.getUpdates$().subscribe({
|
||||
const updateSubscription = filterChanges.subscribe({
|
||||
next: () => {
|
||||
$scope.model.filters = queryFilter.getFilters();
|
||||
$scope.model.filters = filterManager.getFilters();
|
||||
$scope.model.query = queryStringManager.getQuery();
|
||||
dashboardStateManager.applyFilters($scope.model.query, $scope.model.filters);
|
||||
if (dashboardContainer) {
|
||||
dashboardContainer.updateInput({ filters: $scope.model.filters });
|
||||
dashboardContainer.updateInput({
|
||||
filters: $scope.model.filters,
|
||||
query: $scope.model.query,
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -199,10 +199,11 @@ export const castEsToKbnFieldTypeName: (esType: ES_FIELD_TYPES | string) => KBN_
|
|||
// Warning: (ae-missing-release-tag) "connectToQueryState" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public
|
||||
export const connectToQueryState: <S extends QueryState>({ timefilter: { timefilter }, filterManager, state$, }: Pick<QueryStart | QuerySetup, 'timefilter' | 'filterManager' | 'state$'>, stateContainer: BaseStateContainer<S>, syncConfig: {
|
||||
export const connectToQueryState: <S extends QueryState>({ timefilter: { timefilter }, filterManager, queryString, state$, }: Pick<QueryStart | QuerySetup, 'timefilter' | 'filterManager' | 'queryString' | 'state$'>, stateContainer: BaseStateContainer<S>, syncConfig: {
|
||||
time?: boolean;
|
||||
refreshInterval?: boolean;
|
||||
filters?: FilterStateStore | boolean;
|
||||
query?: boolean;
|
||||
}) => () => void;
|
||||
|
||||
// Warning: (ae-missing-release-tag) "createSavedQueryService" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
|
@ -1397,6 +1398,8 @@ export interface QueryState {
|
|||
// (undocumented)
|
||||
filters?: Filter[];
|
||||
// (undocumented)
|
||||
query?: Query;
|
||||
// (undocumented)
|
||||
refreshInterval?: RefreshInterval;
|
||||
// (undocumented)
|
||||
time?: TimeRange;
|
||||
|
@ -1771,7 +1774,7 @@ export type StatefulSearchBarProps = SearchBarOwnProps & {
|
|||
// Warning: (ae-missing-release-tag) "syncQueryStateWithUrl" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public
|
||||
export const syncQueryStateWithUrl: (query: Pick<QueryStart | QuerySetup, 'filterManager' | 'timefilter' | 'state$'>, kbnUrlStateStorage: IKbnUrlStateStorage) => {
|
||||
export const syncQueryStateWithUrl: (query: Pick<QueryStart | QuerySetup, 'filterManager' | 'timefilter' | 'queryString' | 'state$'>, kbnUrlStateStorage: IKbnUrlStateStorage) => {
|
||||
stop: () => void;
|
||||
hasInheritedQueryFromUrl: boolean;
|
||||
};
|
||||
|
@ -1919,7 +1922,7 @@ export const UI_SETTINGS: {
|
|||
// src/plugins/data/public/index.ts:391:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:392:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:395: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:41:60 - (ae-forgotten-export) The symbol "FilterStateStore" 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/types.ts:54:5 - (ae-forgotten-export) The symbol "createFiltersFromValueClickAction" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/types.ts:55:5 - (ae-forgotten-export) The symbol "createFiltersFromRangeSelectAction" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/types.ts:63:5 - (ae-forgotten-export) The symbol "IndexPatternSelectProps" needs to be exported by the entry point index.d.ts
|
||||
|
|
|
@ -21,6 +21,7 @@ import { Observable } from 'rxjs';
|
|||
import { QueryService, QuerySetup, QueryStart } from '.';
|
||||
import { timefilterServiceMock } from './timefilter/timefilter_service.mock';
|
||||
import { createFilterManagerMock } from './filter_manager/filter_manager.mock';
|
||||
import { queryStringManagerMock } from './query_string/query_string_manager.mock';
|
||||
|
||||
type QueryServiceClientContract = PublicMethodsOf<QueryService>;
|
||||
|
||||
|
@ -28,6 +29,7 @@ const createSetupContractMock = () => {
|
|||
const setupContract: jest.Mocked<QuerySetup> = {
|
||||
filterManager: createFilterManagerMock(),
|
||||
timefilter: timefilterServiceMock.createSetupContract(),
|
||||
queryString: queryStringManagerMock.createSetupContract(),
|
||||
state$: new Observable(),
|
||||
};
|
||||
|
||||
|
@ -38,6 +40,7 @@ const createStartContractMock = () => {
|
|||
const startContract: jest.Mocked<QueryStart> = {
|
||||
addToQueryLog: jest.fn(),
|
||||
filterManager: createFilterManagerMock(),
|
||||
queryString: queryStringManagerMock.createStartContract(),
|
||||
savedQueries: jest.fn() as any,
|
||||
state$: new Observable(),
|
||||
timefilter: timefilterServiceMock.createStartContract(),
|
||||
|
|
|
@ -25,6 +25,7 @@ import { createAddToQueryLog } from './lib';
|
|||
import { TimefilterService, TimefilterSetup } from './timefilter';
|
||||
import { createSavedQueryService } from './saved_query/saved_query_service';
|
||||
import { createQueryStateObservable } from './state_sync/create_global_query_observable';
|
||||
import { QueryStringManager, QueryStringContract } from './query_string';
|
||||
|
||||
/**
|
||||
* Query Service
|
||||
|
@ -45,6 +46,7 @@ interface QueryServiceStartDependencies {
|
|||
export class QueryService {
|
||||
filterManager!: FilterManager;
|
||||
timefilter!: TimefilterSetup;
|
||||
queryStringManager!: QueryStringContract;
|
||||
|
||||
state$!: ReturnType<typeof createQueryStateObservable>;
|
||||
|
||||
|
@ -57,14 +59,18 @@ export class QueryService {
|
|||
storage,
|
||||
});
|
||||
|
||||
this.queryStringManager = new QueryStringManager(storage, uiSettings);
|
||||
|
||||
this.state$ = createQueryStateObservable({
|
||||
filterManager: this.filterManager,
|
||||
timefilter: this.timefilter,
|
||||
queryString: this.queryStringManager,
|
||||
}).pipe(share());
|
||||
|
||||
return {
|
||||
filterManager: this.filterManager,
|
||||
timefilter: this.timefilter,
|
||||
queryString: this.queryStringManager,
|
||||
state$: this.state$,
|
||||
};
|
||||
}
|
||||
|
@ -76,6 +82,7 @@ export class QueryService {
|
|||
uiSettings,
|
||||
}),
|
||||
filterManager: this.filterManager,
|
||||
queryString: this.queryStringManager,
|
||||
savedQueries: createSavedQueryService(savedObjectsClient),
|
||||
state$: this.state$,
|
||||
timefilter: this.timefilter,
|
||||
|
|
20
src/plugins/data/public/query/query_string/index.ts
Normal file
20
src/plugins/data/public/query/query_string/index.ts
Normal file
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
export { QueryStringContract, QueryStringManager } from './query_string_manager';
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { QueryStringContract } from '.';
|
||||
|
||||
const createSetupContractMock = () => {
|
||||
const queryStringManagerMock: jest.Mocked<QueryStringContract> = {
|
||||
getQuery: jest.fn(),
|
||||
setQuery: jest.fn(),
|
||||
getUpdates$: jest.fn(),
|
||||
getDefaultQuery: jest.fn(),
|
||||
formatQuery: jest.fn(),
|
||||
clearQuery: jest.fn(),
|
||||
};
|
||||
return queryStringManagerMock;
|
||||
};
|
||||
|
||||
export const queryStringManagerMock = {
|
||||
createSetupContract: createSetupContractMock,
|
||||
createStartContract: createSetupContractMock,
|
||||
};
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import _ from 'lodash';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { CoreStart } from 'kibana/public';
|
||||
import { IStorageWrapper } from 'src/plugins/kibana_utils/public';
|
||||
import { Query, UI_SETTINGS } from '../../../common';
|
||||
|
||||
export class QueryStringManager {
|
||||
private query$: BehaviorSubject<Query>;
|
||||
|
||||
constructor(
|
||||
private readonly storage: IStorageWrapper,
|
||||
private readonly uiSettings: CoreStart['uiSettings']
|
||||
) {
|
||||
this.query$ = new BehaviorSubject<Query>(this.getDefaultQuery());
|
||||
}
|
||||
|
||||
private getDefaultLanguage() {
|
||||
return (
|
||||
this.storage.get('kibana.userQueryLanguage') ||
|
||||
this.uiSettings.get(UI_SETTINGS.SEARCH_QUERY_LANGUAGE)
|
||||
);
|
||||
}
|
||||
|
||||
public getDefaultQuery() {
|
||||
return {
|
||||
query: '',
|
||||
language: this.getDefaultLanguage(),
|
||||
};
|
||||
}
|
||||
|
||||
public formatQuery(query: Query | string | undefined): Query {
|
||||
if (!query) {
|
||||
return this.getDefaultQuery();
|
||||
} else if (typeof query === 'string') {
|
||||
return {
|
||||
query,
|
||||
language: this.getDefaultLanguage(),
|
||||
};
|
||||
} else {
|
||||
return query;
|
||||
}
|
||||
}
|
||||
|
||||
public getUpdates$ = () => {
|
||||
return this.query$.asObservable();
|
||||
};
|
||||
|
||||
public getQuery = (): Query => {
|
||||
return this.query$.getValue();
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates the query.
|
||||
* @param {Query} query
|
||||
*/
|
||||
public setQuery = (query: Query) => {
|
||||
const curQuery = this.query$.getValue();
|
||||
if (query?.language !== curQuery.language || query?.query !== curQuery.query) {
|
||||
this.query$.next(query);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Resets the query to the default one.
|
||||
*/
|
||||
public clearQuery = () => {
|
||||
this.setQuery(this.getDefaultQuery());
|
||||
};
|
||||
}
|
||||
|
||||
export type QueryStringContract = PublicMethodsOf<QueryStringManager>;
|
|
@ -48,6 +48,8 @@ setupMock.uiSettings.get.mockImplementation((key: string) => {
|
|||
switch (key) {
|
||||
case UI_SETTINGS.FILTERS_PINNED_BY_DEFAULT:
|
||||
return true;
|
||||
case UI_SETTINGS.SEARCH_QUERY_LANGUAGE:
|
||||
return 'kuery';
|
||||
case 'timepicker:timeDefaults':
|
||||
return { from: 'now-15m', to: 'now' };
|
||||
case UI_SETTINGS.TIMEPICKER_REFRESH_INTERVAL_DEFAULTS:
|
||||
|
|
|
@ -35,15 +35,24 @@ export const connectToQueryState = <S extends QueryState>(
|
|||
{
|
||||
timefilter: { timefilter },
|
||||
filterManager,
|
||||
queryString,
|
||||
state$,
|
||||
}: Pick<QueryStart | QuerySetup, 'timefilter' | 'filterManager' | 'state$'>,
|
||||
}: Pick<QueryStart | QuerySetup, 'timefilter' | 'filterManager' | 'queryString' | 'state$'>,
|
||||
stateContainer: BaseStateContainer<S>,
|
||||
syncConfig: { time?: boolean; refreshInterval?: boolean; filters?: FilterStateStore | boolean }
|
||||
syncConfig: {
|
||||
time?: boolean;
|
||||
refreshInterval?: boolean;
|
||||
filters?: FilterStateStore | boolean;
|
||||
query?: boolean;
|
||||
}
|
||||
) => {
|
||||
const syncKeys: Array<keyof QueryStateChange> = [];
|
||||
if (syncConfig.time) {
|
||||
syncKeys.push('time');
|
||||
}
|
||||
if (syncConfig.query) {
|
||||
syncKeys.push('query');
|
||||
}
|
||||
if (syncConfig.refreshInterval) {
|
||||
syncKeys.push('refreshInterval');
|
||||
}
|
||||
|
@ -133,6 +142,9 @@ export const connectToQueryState = <S extends QueryState>(
|
|||
if (syncConfig.time && changes.time) {
|
||||
newState.time = timefilter.getTime();
|
||||
}
|
||||
if (syncConfig.query && changes.query) {
|
||||
newState.query = queryString.getQuery();
|
||||
}
|
||||
if (syncConfig.refreshInterval && changes.refreshInterval) {
|
||||
newState.refreshInterval = timefilter.getRefreshInterval();
|
||||
}
|
||||
|
@ -173,6 +185,13 @@ export const connectToQueryState = <S extends QueryState>(
|
|||
}
|
||||
}
|
||||
|
||||
if (syncConfig.query) {
|
||||
const curQuery = state.query || queryString.getQuery();
|
||||
if (!_.isEqual(curQuery, queryString.getQuery())) {
|
||||
queryString.setQuery(_.cloneDeep(curQuery));
|
||||
}
|
||||
}
|
||||
|
||||
if (syncConfig.filters) {
|
||||
const filters = state.filters || [];
|
||||
if (syncConfig.filters === true) {
|
||||
|
|
|
@ -24,23 +24,31 @@ import { FilterManager } from '../filter_manager';
|
|||
import { QueryState, QueryStateChange } from './index';
|
||||
import { createStateContainer } from '../../../../kibana_utils/public';
|
||||
import { isFilterPinned, compareFilters, COMPARE_ALL_OPTIONS } from '../../../common';
|
||||
import { QueryStringContract } from '../query_string';
|
||||
|
||||
export function createQueryStateObservable({
|
||||
timefilter: { timefilter },
|
||||
filterManager,
|
||||
queryString,
|
||||
}: {
|
||||
timefilter: TimefilterSetup;
|
||||
filterManager: FilterManager;
|
||||
queryString: QueryStringContract;
|
||||
}): Observable<{ changes: QueryStateChange; state: QueryState }> {
|
||||
return new Observable((subscriber) => {
|
||||
const state = createStateContainer<QueryState>({
|
||||
time: timefilter.getTime(),
|
||||
refreshInterval: timefilter.getRefreshInterval(),
|
||||
filters: filterManager.getFilters(),
|
||||
query: queryString.getQuery(),
|
||||
});
|
||||
|
||||
let currentChange: QueryStateChange = {};
|
||||
const subs: Subscription[] = [
|
||||
queryString.getUpdates$().subscribe(() => {
|
||||
currentChange.query = true;
|
||||
state.set({ ...state.get(), query: queryString.getQuery() });
|
||||
}),
|
||||
timefilter.getTimeUpdate$().subscribe(() => {
|
||||
currentChange.time = true;
|
||||
state.set({ ...state.get(), time: timefilter.getTime() });
|
||||
|
|
|
@ -43,6 +43,8 @@ setupMock.uiSettings.get.mockImplementation((key: string) => {
|
|||
return true;
|
||||
case 'timepicker:timeDefaults':
|
||||
return { from: 'now-15m', to: 'now' };
|
||||
case 'search:queryLanguage':
|
||||
return 'kuery';
|
||||
case UI_SETTINGS.TIMEPICKER_REFRESH_INTERVAL_DEFAULTS:
|
||||
return { pause: false, value: 0 };
|
||||
default:
|
||||
|
|
|
@ -35,7 +35,7 @@ const GLOBAL_STATE_STORAGE_KEY = '_g';
|
|||
* @param kbnUrlStateStorage to use for syncing
|
||||
*/
|
||||
export const syncQueryStateWithUrl = (
|
||||
query: Pick<QueryStart | QuerySetup, 'filterManager' | 'timefilter' | 'state$'>,
|
||||
query: Pick<QueryStart | QuerySetup, 'filterManager' | 'timefilter' | 'queryString' | 'state$'>,
|
||||
kbnUrlStateStorage: IKbnUrlStateStorage
|
||||
) => {
|
||||
const {
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { Filter, RefreshInterval, TimeRange } from '../../../common';
|
||||
import { Filter, RefreshInterval, TimeRange, Query } from '../../../common';
|
||||
|
||||
/**
|
||||
* All query state service state
|
||||
|
@ -26,6 +26,7 @@ export interface QueryState {
|
|||
time?: TimeRange;
|
||||
refreshInterval?: RefreshInterval;
|
||||
filters?: Filter[];
|
||||
query?: Query;
|
||||
}
|
||||
|
||||
type QueryStateChangePartial = {
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
|
||||
import _ from 'lodash';
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import { CoreStart } from 'src/core/public';
|
||||
import { IStorageWrapper } from 'src/plugins/kibana_utils/public';
|
||||
import { KibanaContextProvider } from '../../../../kibana_react/public';
|
||||
|
@ -28,7 +28,8 @@ import { useFilterManager } from './lib/use_filter_manager';
|
|||
import { useTimefilter } from './lib/use_timefilter';
|
||||
import { useSavedQuery } from './lib/use_saved_query';
|
||||
import { DataPublicPluginStart } from '../../types';
|
||||
import { Filter, Query, TimeRange, UI_SETTINGS } from '../../../common';
|
||||
import { Filter, Query, TimeRange } from '../../../common';
|
||||
import { useQueryStringManager } from './lib/use_query_string_manager';
|
||||
|
||||
interface StatefulSearchBarDeps {
|
||||
core: CoreStart;
|
||||
|
@ -65,8 +66,7 @@ const defaultOnRefreshChange = (queryService: QueryStart) => {
|
|||
const defaultOnQuerySubmit = (
|
||||
props: StatefulSearchBarProps,
|
||||
queryService: QueryStart,
|
||||
currentQuery: Query,
|
||||
setQueryStringState: Function
|
||||
currentQuery: Query
|
||||
) => {
|
||||
if (!props.useDefaultBehaviors) return props.onQuerySubmit;
|
||||
|
||||
|
@ -78,7 +78,11 @@ const defaultOnQuerySubmit = (
|
|||
!_.isEqual(payload.query, currentQuery);
|
||||
if (isUpdate) {
|
||||
timefilter.setTime(payload.dateRange);
|
||||
setQueryStringState(payload.query);
|
||||
if (payload.query) {
|
||||
queryService.queryString.setQuery(payload.query);
|
||||
} else {
|
||||
queryService.queryString.clearQuery();
|
||||
}
|
||||
} else {
|
||||
// Refresh button triggered for an update
|
||||
if (props.onQuerySubmit)
|
||||
|
@ -121,30 +125,7 @@ export function createSearchBar({ core, storage, data }: StatefulSearchBarDeps)
|
|||
return (props: StatefulSearchBarProps) => {
|
||||
const { useDefaultBehaviors } = props;
|
||||
// Handle queries
|
||||
const queryRef = useRef(props.query);
|
||||
const onQuerySubmitRef = useRef(props.onQuerySubmit);
|
||||
const defaultQuery = {
|
||||
query: '',
|
||||
language:
|
||||
storage.get('kibana.userQueryLanguage') ||
|
||||
core.uiSettings.get(UI_SETTINGS.SEARCH_QUERY_LANGUAGE),
|
||||
};
|
||||
const [query, setQuery] = useState<Query>(props.query || defaultQuery);
|
||||
|
||||
useEffect(() => {
|
||||
if (props.query !== queryRef.current) {
|
||||
queryRef.current = props.query;
|
||||
setQuery(props.query || defaultQuery);
|
||||
}
|
||||
/* eslint-disable-next-line react-hooks/exhaustive-deps */
|
||||
}, [defaultQuery, props.query]);
|
||||
|
||||
useEffect(() => {
|
||||
if (props.onQuerySubmit !== onQuerySubmitRef.current) {
|
||||
onQuerySubmitRef.current = props.onQuerySubmit;
|
||||
}
|
||||
/* eslint-disable-next-line react-hooks/exhaustive-deps */
|
||||
}, [props.onQuerySubmit]);
|
||||
|
||||
// handle service state updates.
|
||||
// i.e. filters being added from a visualization directly to filterManager.
|
||||
|
@ -152,6 +133,10 @@ export function createSearchBar({ core, storage, data }: StatefulSearchBarDeps)
|
|||
filters: props.filters,
|
||||
filterManager: data.query.filterManager,
|
||||
});
|
||||
const { query } = useQueryStringManager({
|
||||
query: props.query,
|
||||
queryStringManager: data.query.queryString,
|
||||
});
|
||||
const { timeRange, refreshInterval } = useTimefilter({
|
||||
dateRangeFrom: props.dateRangeFrom,
|
||||
dateRangeTo: props.dateRangeTo,
|
||||
|
@ -163,10 +148,8 @@ export function createSearchBar({ core, storage, data }: StatefulSearchBarDeps)
|
|||
// Fetch and update UI from saved query
|
||||
const { savedQuery, setSavedQuery, clearSavedQuery } = useSavedQuery({
|
||||
queryService: data.query,
|
||||
setQuery,
|
||||
savedQueryId: props.savedQueryId,
|
||||
notifications: core.notifications,
|
||||
defaultLanguage: defaultQuery.language,
|
||||
});
|
||||
|
||||
// Fire onQuerySubmit on query or timerange change
|
||||
|
@ -210,7 +193,7 @@ export function createSearchBar({ core, storage, data }: StatefulSearchBarDeps)
|
|||
onFiltersUpdated={defaultFiltersUpdated(data.query)}
|
||||
onRefreshChange={defaultOnRefreshChange(data.query)}
|
||||
savedQuery={savedQuery}
|
||||
onQuerySubmit={defaultOnQuerySubmit(props, data.query, query, setQuery)}
|
||||
onQuerySubmit={defaultOnQuerySubmit(props, data.query, query)}
|
||||
onClearSavedQuery={defaultOnClearSavedQuery(props, clearSavedQuery)}
|
||||
onSavedQueryUpdated={defaultOnSavedQueryUpdated(props, setSavedQuery)}
|
||||
onSaved={defaultOnSavedQueryUpdated(props, setSavedQuery)}
|
||||
|
|
|
@ -21,10 +21,8 @@ import { clearStateFromSavedQuery } from './clear_saved_query';
|
|||
|
||||
import { dataPluginMock } from '../../../mocks';
|
||||
import { DataPublicPluginStart } from '../../../types';
|
||||
import { Query } from '../../..';
|
||||
|
||||
describe('clearStateFromSavedQuery', () => {
|
||||
const DEFAULT_LANGUAGE = 'banana';
|
||||
let dataMock: jest.Mocked<DataPublicPluginStart>;
|
||||
|
||||
beforeEach(() => {
|
||||
|
@ -32,19 +30,9 @@ describe('clearStateFromSavedQuery', () => {
|
|||
});
|
||||
|
||||
it('should clear filters and query', async () => {
|
||||
const setQueryState = jest.fn();
|
||||
dataMock.query.filterManager.removeAll = jest.fn();
|
||||
clearStateFromSavedQuery(dataMock.query, setQueryState, DEFAULT_LANGUAGE);
|
||||
expect(setQueryState).toHaveBeenCalled();
|
||||
expect(dataMock.query.filterManager.removeAll).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should use search:queryLanguage', async () => {
|
||||
const setQueryState = jest.fn();
|
||||
dataMock.query.filterManager.removeAll = jest.fn();
|
||||
clearStateFromSavedQuery(dataMock.query, setQueryState, DEFAULT_LANGUAGE);
|
||||
expect(setQueryState).toHaveBeenCalled();
|
||||
expect((setQueryState.mock.calls[0][0] as Query).language).toBe(DEFAULT_LANGUAGE);
|
||||
clearStateFromSavedQuery(dataMock.query);
|
||||
expect(dataMock.query.queryString.clearQuery).toHaveBeenCalled();
|
||||
expect(dataMock.query.filterManager.removeAll).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -18,14 +18,7 @@
|
|||
*/
|
||||
import { QueryStart } from '../../../query';
|
||||
|
||||
export const clearStateFromSavedQuery = (
|
||||
queryService: QueryStart,
|
||||
setQueryStringState: Function,
|
||||
defaultLanguage: string
|
||||
) => {
|
||||
export const clearStateFromSavedQuery = (queryService: QueryStart) => {
|
||||
queryService.filterManager.removeAll();
|
||||
setQueryStringState({
|
||||
query: '',
|
||||
language: defaultLanguage,
|
||||
});
|
||||
queryService.queryString.clearQuery();
|
||||
};
|
||||
|
|
|
@ -47,37 +47,34 @@ describe('populateStateFromSavedQuery', () => {
|
|||
});
|
||||
|
||||
it('should set query', async () => {
|
||||
const setQueryState = jest.fn();
|
||||
const savedQuery: SavedQuery = {
|
||||
...baseSavedQuery,
|
||||
};
|
||||
populateStateFromSavedQuery(dataMock.query, setQueryState, savedQuery);
|
||||
expect(setQueryState).toHaveBeenCalled();
|
||||
populateStateFromSavedQuery(dataMock.query, savedQuery);
|
||||
expect(dataMock.query.queryString.setQuery).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should set filters', async () => {
|
||||
const setQueryState = jest.fn();
|
||||
const savedQuery: SavedQuery = {
|
||||
...baseSavedQuery,
|
||||
};
|
||||
const f1 = getFilter(FilterStateStore.APP_STATE, false, false, 'age', 34);
|
||||
savedQuery.attributes.filters = [f1];
|
||||
populateStateFromSavedQuery(dataMock.query, setQueryState, savedQuery);
|
||||
expect(setQueryState).toHaveBeenCalled();
|
||||
populateStateFromSavedQuery(dataMock.query, savedQuery);
|
||||
expect(dataMock.query.queryString.setQuery).toHaveBeenCalled();
|
||||
expect(dataMock.query.filterManager.setFilters).toHaveBeenCalledWith([f1]);
|
||||
});
|
||||
|
||||
it('should preserve global filters', async () => {
|
||||
const globalFilter = getFilter(FilterStateStore.GLOBAL_STATE, false, false, 'age', 34);
|
||||
dataMock.query.filterManager.getGlobalFilters = jest.fn().mockReturnValue([globalFilter]);
|
||||
const setQueryState = jest.fn();
|
||||
const savedQuery: SavedQuery = {
|
||||
...baseSavedQuery,
|
||||
};
|
||||
const f1 = getFilter(FilterStateStore.APP_STATE, false, false, 'age', 34);
|
||||
savedQuery.attributes.filters = [f1];
|
||||
populateStateFromSavedQuery(dataMock.query, setQueryState, savedQuery);
|
||||
expect(setQueryState).toHaveBeenCalled();
|
||||
populateStateFromSavedQuery(dataMock.query, savedQuery);
|
||||
expect(dataMock.query.queryString.setQuery).toHaveBeenCalled();
|
||||
expect(dataMock.query.filterManager.setFilters).toHaveBeenCalledWith([globalFilter, f1]);
|
||||
});
|
||||
|
||||
|
@ -97,7 +94,7 @@ describe('populateStateFromSavedQuery', () => {
|
|||
dataMock.query.timefilter.timefilter.setTime = jest.fn();
|
||||
dataMock.query.timefilter.timefilter.setRefreshInterval = jest.fn();
|
||||
|
||||
populateStateFromSavedQuery(dataMock.query, jest.fn(), savedQuery);
|
||||
populateStateFromSavedQuery(dataMock.query, savedQuery);
|
||||
|
||||
expect(dataMock.query.timefilter.timefilter.setTime).toHaveBeenCalledWith({
|
||||
from: savedQuery.attributes.timefilter.from,
|
||||
|
|
|
@ -19,14 +19,11 @@
|
|||
|
||||
import { QueryStart, SavedQuery } from '../../../query';
|
||||
|
||||
export const populateStateFromSavedQuery = (
|
||||
queryService: QueryStart,
|
||||
setQueryStringState: Function,
|
||||
savedQuery: SavedQuery
|
||||
) => {
|
||||
export const populateStateFromSavedQuery = (queryService: QueryStart, savedQuery: SavedQuery) => {
|
||||
const {
|
||||
timefilter: { timefilter },
|
||||
filterManager,
|
||||
queryString,
|
||||
} = queryService;
|
||||
// timefilter
|
||||
if (savedQuery.attributes.timefilter) {
|
||||
|
@ -40,7 +37,7 @@ export const populateStateFromSavedQuery = (
|
|||
}
|
||||
|
||||
// query string
|
||||
setQueryStringState(savedQuery.attributes.query);
|
||||
queryString.setQuery(savedQuery.attributes.query);
|
||||
|
||||
// filters
|
||||
const savedQueryFilters = savedQuery.attributes.filters || [];
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { Query } from '../../..';
|
||||
import { QueryStringContract } from '../../../query/query_string';
|
||||
|
||||
interface UseQueryStringProps {
|
||||
query?: Query;
|
||||
queryStringManager: QueryStringContract;
|
||||
}
|
||||
|
||||
export const useQueryStringManager = (props: UseQueryStringProps) => {
|
||||
// Filters should be either what's passed in the initial state or the current state of the filter manager
|
||||
const [query, setQuery] = useState<Query>(props.query || props.queryStringManager.getQuery());
|
||||
useEffect(() => {
|
||||
const subscriptions = new Subscription();
|
||||
|
||||
subscriptions.add(
|
||||
props.queryStringManager.getUpdates$().subscribe({
|
||||
next: () => {
|
||||
const newQuery = props.queryStringManager.getQuery();
|
||||
setQuery(newQuery);
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
return () => {
|
||||
subscriptions.unsubscribe();
|
||||
};
|
||||
}, [props.queryStringManager]);
|
||||
|
||||
return { query };
|
||||
};
|
|
@ -27,10 +27,8 @@ import { clearStateFromSavedQuery } from './clear_saved_query';
|
|||
|
||||
interface UseSavedQueriesProps {
|
||||
queryService: DataPublicPluginStart['query'];
|
||||
setQuery: Function;
|
||||
notifications: CoreStart['notifications'];
|
||||
savedQueryId?: string;
|
||||
defaultLanguage: string;
|
||||
}
|
||||
|
||||
interface UseSavedQueriesReturn {
|
||||
|
@ -41,7 +39,6 @@ interface UseSavedQueriesReturn {
|
|||
|
||||
export const useSavedQuery = (props: UseSavedQueriesProps): UseSavedQueriesReturn => {
|
||||
// Handle saved queries
|
||||
const defaultLanguage = props.defaultLanguage;
|
||||
const [savedQuery, setSavedQuery] = useState<SavedQuery | undefined>();
|
||||
|
||||
// Effect is used to convert a saved query id into an object
|
||||
|
@ -53,12 +50,12 @@ export const useSavedQuery = (props: UseSavedQueriesProps): UseSavedQueriesRetur
|
|||
// Make sure we set the saved query to the most recent one
|
||||
if (newSavedQuery && newSavedQuery.id === savedQueryId) {
|
||||
setSavedQuery(newSavedQuery);
|
||||
populateStateFromSavedQuery(props.queryService, props.setQuery, newSavedQuery);
|
||||
populateStateFromSavedQuery(props.queryService, newSavedQuery);
|
||||
}
|
||||
} catch (error) {
|
||||
// Clear saved query
|
||||
setSavedQuery(undefined);
|
||||
clearStateFromSavedQuery(props.queryService, props.setQuery, defaultLanguage);
|
||||
clearStateFromSavedQuery(props.queryService);
|
||||
// notify of saving error
|
||||
props.notifications.toasts.addWarning({
|
||||
title: i18n.translate('data.search.unableToGetSavedQueryToastTitle', {
|
||||
|
@ -73,23 +70,21 @@ export const useSavedQuery = (props: UseSavedQueriesProps): UseSavedQueriesRetur
|
|||
if (props.savedQueryId) fetchSavedQuery(props.savedQueryId);
|
||||
else setSavedQuery(undefined);
|
||||
}, [
|
||||
defaultLanguage,
|
||||
props.notifications.toasts,
|
||||
props.queryService,
|
||||
props.queryService.savedQueries,
|
||||
props.savedQueryId,
|
||||
props.setQuery,
|
||||
]);
|
||||
|
||||
return {
|
||||
savedQuery,
|
||||
setSavedQuery: (q: SavedQuery) => {
|
||||
setSavedQuery(q);
|
||||
populateStateFromSavedQuery(props.queryService, props.setQuery, q);
|
||||
populateStateFromSavedQuery(props.queryService, q);
|
||||
},
|
||||
clearSavedQuery: () => {
|
||||
setSavedQuery(undefined);
|
||||
clearStateFromSavedQuery(props.queryService, props.setQuery, defaultLanguage);
|
||||
clearStateFromSavedQuery(props.queryService);
|
||||
},
|
||||
};
|
||||
};
|
||||
|
|
|
@ -6,9 +6,8 @@
|
|||
app-name="'discover'"
|
||||
config="topNavMenu"
|
||||
index-patterns="[indexPattern]"
|
||||
on-query-submit="updateQuery"
|
||||
on-query-submit="handleRefresh"
|
||||
on-saved-query-id-change="updateSavedQueryId"
|
||||
query="state.query"
|
||||
saved-query-id="state.savedQuery"
|
||||
screen-title="screenTitle"
|
||||
show-date-picker="indexPattern.isTimeBased()"
|
||||
|
|
|
@ -70,9 +70,7 @@ import {
|
|||
indexPatterns as indexPatternsUtils,
|
||||
connectToQueryState,
|
||||
syncQueryStateWithUrl,
|
||||
getDefaultQuery,
|
||||
search,
|
||||
UI_SETTINGS,
|
||||
} from '../../../../data/public';
|
||||
import { getIndexPatternId } from '../helpers/get_index_pattern_id';
|
||||
import { addFatalError } from '../../../../kibana_legacy/public';
|
||||
|
@ -191,16 +189,7 @@ app.directive('discoverApp', function () {
|
|||
};
|
||||
});
|
||||
|
||||
function discoverController(
|
||||
$element,
|
||||
$route,
|
||||
$scope,
|
||||
$timeout,
|
||||
$window,
|
||||
Promise,
|
||||
localStorage,
|
||||
uiCapabilities
|
||||
) {
|
||||
function discoverController($element, $route, $scope, $timeout, $window, Promise, uiCapabilities) {
|
||||
const { isDefault: isDefaultType } = indexPatternsUtils;
|
||||
const subscriptions = new Subscription();
|
||||
const $fetchObservable = new Subject();
|
||||
|
@ -246,11 +235,15 @@ function discoverController(
|
|||
|
||||
// sync initial app filters from state to filterManager
|
||||
filterManager.setAppFilters(_.cloneDeep(appStateContainer.getState().filters));
|
||||
data.query.queryString.setQuery(appStateContainer.getState().query);
|
||||
|
||||
const stopSyncingQueryAppStateWithStateContainer = connectToQueryState(
|
||||
data.query,
|
||||
appStateContainer,
|
||||
{ filters: esFilters.FilterStateStore.APP_STATE }
|
||||
{
|
||||
filters: esFilters.FilterStateStore.APP_STATE,
|
||||
query: true,
|
||||
}
|
||||
);
|
||||
|
||||
const appStateUnsubscribe = appStateContainer.subscribe(async (newState) => {
|
||||
|
@ -262,7 +255,7 @@ function discoverController(
|
|||
$scope.state = { ...newState };
|
||||
|
||||
// detect changes that should trigger fetching of new data
|
||||
const changes = ['interval', 'sort', 'query'].filter(
|
||||
const changes = ['interval', 'sort'].filter(
|
||||
(prop) => !_.isEqual(newStatePartial[prop], oldStatePartial[prop])
|
||||
);
|
||||
|
||||
|
@ -593,12 +586,7 @@ function discoverController(
|
|||
};
|
||||
|
||||
function getStateDefaults() {
|
||||
const query =
|
||||
$scope.searchSource.getField('query') ||
|
||||
getDefaultQuery(
|
||||
localStorage.get('kibana.userQueryLanguage') ||
|
||||
config.get(UI_SETTINGS.SEARCH_QUERY_LANGUAGE)
|
||||
);
|
||||
const query = $scope.searchSource.getField('query') || data.query.queryString.getDefaultQuery();
|
||||
return {
|
||||
query,
|
||||
sort: getSortArray(savedSearch.sort, $scope.indexPattern),
|
||||
|
@ -635,12 +623,7 @@ function discoverController(
|
|||
|
||||
const init = _.once(() => {
|
||||
$scope.updateDataSource().then(async () => {
|
||||
const searchBarChanges = merge(
|
||||
timefilter.getAutoRefreshFetch$(),
|
||||
timefilter.getFetch$(),
|
||||
filterManager.getFetches$(),
|
||||
$fetchObservable
|
||||
).pipe(debounceTime(100));
|
||||
const searchBarChanges = merge(data.query.state$, $fetchObservable).pipe(debounceTime(100));
|
||||
|
||||
subscriptions.add(
|
||||
subscribeWithScope(
|
||||
|
@ -824,9 +807,8 @@ function discoverController(
|
|||
});
|
||||
};
|
||||
|
||||
$scope.updateQuery = function ({ query }, isUpdate = true) {
|
||||
if (!_.isEqual(query, appStateContainer.getState().query) || isUpdate === false) {
|
||||
setAppState({ query });
|
||||
$scope.handleRefresh = function (_payload, isUpdate) {
|
||||
if (isUpdate === false) {
|
||||
$fetchObservable.next();
|
||||
}
|
||||
};
|
||||
|
@ -976,7 +958,7 @@ function discoverController(
|
|||
config.get(SORT_DEFAULT_ORDER_SETTING)
|
||||
)
|
||||
)
|
||||
.setField('query', $scope.state.query || null)
|
||||
.setField('query', data.query.queryString.getQuery() || null)
|
||||
.setField('filter', filterManager.getFilters());
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
|
|
@ -35,12 +35,7 @@ import {
|
|||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import {
|
||||
esQuery,
|
||||
IndexPattern,
|
||||
Query,
|
||||
UI_SETTINGS,
|
||||
} from '../../../../../../../plugins/data/public';
|
||||
import { esQuery, IndexPattern, Query } from '../../../../../../../plugins/data/public';
|
||||
import { context as contextType } from '../../../../../../kibana_react/public';
|
||||
import { IndexPatternManagmentContextValue } from '../../../../types';
|
||||
import { ExecuteScript } from '../../types';
|
||||
|
@ -248,10 +243,7 @@ export class TestScript extends Component<TestScriptProps, TestScriptState> {
|
|||
showFilterBar={false}
|
||||
showDatePicker={false}
|
||||
showQueryInput={true}
|
||||
query={{
|
||||
language: this.context.services.uiSettings.get(UI_SETTINGS.SEARCH_QUERY_LANGUAGE),
|
||||
query: '',
|
||||
}}
|
||||
query={this.context.services.data.query.queryString.getDefaultQuery()}
|
||||
onQuerySubmit={this.previewScript}
|
||||
indexPatterns={[this.props.indexPattern]}
|
||||
customSubmitButton={
|
||||
|
|
|
@ -23,7 +23,7 @@ import { htmlIdGenerator, EuiButton, EuiSpacer } from '@elastic/eui';
|
|||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { useMount } from 'react-use';
|
||||
|
||||
import { Query, UI_SETTINGS } from '../../../../data/public';
|
||||
import { Query } from '../../../../data/public';
|
||||
import { useKibana } from '../../../../kibana_react/public';
|
||||
import { FilterRow } from './filter';
|
||||
import { AggParamEditorProps } from '../agg_param_props';
|
||||
|
@ -70,7 +70,7 @@ function FiltersParamEditor({ agg, value = [], setValue }: AggParamEditorProps<F
|
|||
updateFilters([
|
||||
...filters,
|
||||
{
|
||||
input: { query: '', language: services.uiSettings.get(UI_SETTINGS.SEARCH_QUERY_LANGUAGE) },
|
||||
input: services.data.query.queryString.getDefaultQuery(),
|
||||
label: '',
|
||||
id: generateId(),
|
||||
},
|
||||
|
|
|
@ -18,10 +18,8 @@
|
|||
*/
|
||||
|
||||
import React, { memo, useCallback, useMemo, useState, useEffect } from 'react';
|
||||
import { isEqual } from 'lodash';
|
||||
|
||||
import { OverlayRef } from 'kibana/public';
|
||||
import { Query } from 'src/plugins/data/public';
|
||||
import { useKibana } from '../../../../kibana_react/public';
|
||||
import {
|
||||
VisualizeServices,
|
||||
|
@ -68,15 +66,13 @@ const TopNav = ({
|
|||
setInspectorSession(session);
|
||||
}, [embeddableHandler]);
|
||||
|
||||
const updateQuery = useCallback(
|
||||
({ query }: { query?: Query }) => {
|
||||
if (!isEqual(currentAppState.query, query)) {
|
||||
stateContainer.transitions.set('query', query || currentAppState.query);
|
||||
} else {
|
||||
const handleRefresh = useCallback(
|
||||
(_payload: any, isUpdate?: boolean) => {
|
||||
if (isUpdate === false) {
|
||||
savedVisInstance.embeddableHandler.reload();
|
||||
}
|
||||
},
|
||||
[currentAppState.query, savedVisInstance.embeddableHandler, stateContainer.transitions]
|
||||
[savedVisInstance.embeddableHandler]
|
||||
);
|
||||
|
||||
const config = useMemo(() => {
|
||||
|
@ -149,8 +145,7 @@ const TopNav = ({
|
|||
<TopNavMenu
|
||||
appName={APP_NAME}
|
||||
config={config}
|
||||
query={currentAppState.query}
|
||||
onQuerySubmit={updateQuery}
|
||||
onQuerySubmit={handleRefresh}
|
||||
savedQueryId={currentAppState.savedQuery}
|
||||
onSavedQueryIdChange={stateContainer.transitions.updateSavedQuery}
|
||||
indexPatterns={indexPattern ? [indexPattern] : undefined}
|
||||
|
|
|
@ -105,10 +105,16 @@ describe('useEditorUpdates', () => {
|
|||
to: 'now',
|
||||
};
|
||||
mockFilters = ['mockFilters'];
|
||||
const mockQuery = {
|
||||
query: '',
|
||||
language: 'kuery',
|
||||
};
|
||||
// @ts-expect-error
|
||||
mockServices.data.query.timefilter.timefilter.getTime.mockImplementation(() => timeRange);
|
||||
// @ts-expect-error
|
||||
mockServices.data.query.filterManager.getFilters.mockImplementation(() => mockFilters);
|
||||
// @ts-expect-error
|
||||
mockServices.data.query.queryString.getQuery.mockImplementation(() => mockQuery);
|
||||
});
|
||||
|
||||
test('should set up current app state and render the editor', () => {
|
||||
|
|
|
@ -20,9 +20,7 @@
|
|||
import { useEffect, useState } from 'react';
|
||||
import { isEqual } from 'lodash';
|
||||
import { EventEmitter } from 'events';
|
||||
import { merge } from 'rxjs';
|
||||
|
||||
import { migrateLegacyQuery } from '../../../../../kibana_legacy/public';
|
||||
import {
|
||||
VisualizeServices,
|
||||
VisualizeAppState,
|
||||
|
@ -47,6 +45,8 @@ export const useEditorUpdates = (
|
|||
const {
|
||||
timefilter: { timefilter },
|
||||
filterManager,
|
||||
queryString,
|
||||
state$,
|
||||
} = services.data.query;
|
||||
const { embeddableHandler, savedVis, savedSearch, vis } = savedVisInstance;
|
||||
const initialState = appState.getState();
|
||||
|
@ -60,7 +60,7 @@ export const useEditorUpdates = (
|
|||
uiState: vis.uiState,
|
||||
timeRange: timefilter.getTime(),
|
||||
filters: filterManager.getFilters(),
|
||||
query: appState.getState().query,
|
||||
query: queryString.getQuery(),
|
||||
linked: !!vis.data.savedSearchId,
|
||||
savedSearch,
|
||||
});
|
||||
|
@ -68,17 +68,12 @@ export const useEditorUpdates = (
|
|||
embeddableHandler.updateInput({
|
||||
timeRange: timefilter.getTime(),
|
||||
filters: filterManager.getFilters(),
|
||||
query: appState.getState().query,
|
||||
query: queryString.getQuery(),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const subscriptions = merge(
|
||||
timefilter.getTimeUpdate$(),
|
||||
timefilter.getAutoRefreshFetch$(),
|
||||
timefilter.getFetch$(),
|
||||
filterManager.getFetches$()
|
||||
).subscribe({
|
||||
const subscriptions = state$.subscribe({
|
||||
next: reloadVisualization,
|
||||
error: services.fatalErrors.add,
|
||||
});
|
||||
|
@ -116,10 +111,6 @@ export const useEditorUpdates = (
|
|||
// and initializing different visualizations
|
||||
return;
|
||||
}
|
||||
const newQuery = migrateLegacyQuery(state.query);
|
||||
if (!isEqual(state.query, newQuery)) {
|
||||
appState.transitions.set('query', newQuery);
|
||||
}
|
||||
|
||||
if (!isEqual(state.uiState, vis.uiState.getChanges())) {
|
||||
vis.uiState.set(state.uiState);
|
||||
|
|
|
@ -96,6 +96,7 @@ describe('useVisualizeAppState', () => {
|
|||
);
|
||||
expect(connectToQueryState).toHaveBeenCalledWith(mockServices.data.query, expect.any(Object), {
|
||||
filters: 'appState',
|
||||
query: true,
|
||||
});
|
||||
expect(result.current).toEqual({
|
||||
appState: stateContainer,
|
||||
|
|
|
@ -24,6 +24,7 @@ import { EventEmitter } from 'events';
|
|||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { MarkdownSimple, toMountPoint } from '../../../../../kibana_react/public';
|
||||
import { migrateLegacyQuery } from '../../../../../kibana_legacy/public';
|
||||
import { esFilters, connectToQueryState } from '../../../../../data/public';
|
||||
import { VisualizeServices, VisualizeAppStateContainer, SavedVisInstance } from '../../types';
|
||||
import { visStateToEditorState } from '../utils';
|
||||
|
@ -61,19 +62,35 @@ export const useVisualizeAppState = (
|
|||
|
||||
eventEmitter.on('dirtyStateChange', onDirtyStateChange);
|
||||
|
||||
const { filterManager } = services.data.query;
|
||||
// sync initial app filters from state to filterManager
|
||||
const { filterManager, queryString } = services.data.query;
|
||||
// sync initial app state from state to managers
|
||||
filterManager.setAppFilters(cloneDeep(stateContainer.getState().filters));
|
||||
// setup syncing of app filters between appState and filterManager
|
||||
queryString.setQuery(migrateLegacyQuery(stateContainer.getState().query));
|
||||
|
||||
// setup syncing of app filters between appState and query services
|
||||
const stopSyncingAppFilters = connectToQueryState(
|
||||
services.data.query,
|
||||
{
|
||||
set: ({ filters }) => stateContainer.transitions.set('filters', filters),
|
||||
get: () => ({ filters: stateContainer.getState().filters }),
|
||||
state$: stateContainer.state$.pipe(map((state) => ({ filters: state.filters }))),
|
||||
set: ({ filters, query }) => {
|
||||
stateContainer.transitions.set('filters', filters);
|
||||
stateContainer.transitions.set('query', query);
|
||||
},
|
||||
get: () => {
|
||||
return {
|
||||
filters: stateContainer.getState().filters,
|
||||
query: stateContainer.getState().query,
|
||||
};
|
||||
},
|
||||
state$: stateContainer.state$.pipe(
|
||||
map((state) => ({
|
||||
filters: state.filters,
|
||||
query: state.query,
|
||||
}))
|
||||
),
|
||||
},
|
||||
{
|
||||
filters: esFilters.FilterStateStore.APP_STATE,
|
||||
query: true,
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { ChromeStart, DocLinksStart } from 'kibana/public';
|
||||
import { Filter, UI_SETTINGS } from '../../../../data/public';
|
||||
import { Filter } from '../../../../data/public';
|
||||
import { VisualizeServices, SavedVisInstance } from '../types';
|
||||
|
||||
export const addHelpMenuToAppChrome = (chrome: ChromeStart, docLinks: DocLinksStart) => {
|
||||
|
@ -49,12 +49,9 @@ export const addBadgeToAppChrome = (chrome: ChromeStart) => {
|
|||
});
|
||||
};
|
||||
|
||||
export const getDefaultQuery = ({ localStorage, uiSettings }: VisualizeServices) => ({
|
||||
query: '',
|
||||
language:
|
||||
localStorage.get('kibana.userQueryLanguage') ||
|
||||
uiSettings.get(UI_SETTINGS.SEARCH_QUERY_LANGUAGE),
|
||||
});
|
||||
export const getDefaultQuery = ({ data }: VisualizeServices) => {
|
||||
return data.query.queryString.getDefaultQuery();
|
||||
};
|
||||
|
||||
export const visStateToEditorState = (
|
||||
{ vis, savedVis }: SavedVisInstance,
|
||||
|
|
|
@ -563,6 +563,10 @@ export default function ({ getService, getPageObjects }) {
|
|||
|
||||
it('should display updated scaled label text after time range is changed', async () => {
|
||||
await PageObjects.visEditor.setInterval('Millisecond');
|
||||
|
||||
// Apply interval
|
||||
await testSubjects.clickWhenNotDisabled('visualizeEditorRenderButton');
|
||||
|
||||
const isHelperScaledLabelExists = await find.existsByCssSelector(
|
||||
'[data-test-subj="currentlyScaledText"]'
|
||||
);
|
||||
|
|
|
@ -95,6 +95,14 @@ function createMockFilterManager() {
|
|||
};
|
||||
}
|
||||
|
||||
function createMockQueryString() {
|
||||
return {
|
||||
getQuery: jest.fn(() => ({ query: '', language: 'kuery' })),
|
||||
setQuery: jest.fn(),
|
||||
getDefaultQuery: jest.fn(() => ({ query: '', language: 'kuery' })),
|
||||
};
|
||||
}
|
||||
|
||||
function createMockTimefilter() {
|
||||
const unsubscribe = jest.fn();
|
||||
|
||||
|
@ -148,6 +156,7 @@ describe('Lens App', () => {
|
|||
timefilter: {
|
||||
timefilter: createMockTimefilter(),
|
||||
},
|
||||
queryString: createMockQueryString(),
|
||||
state$: new Observable(),
|
||||
},
|
||||
indexPatterns: {
|
||||
|
|
|
@ -36,7 +36,6 @@ import {
|
|||
IndexPattern as IndexPatternInstance,
|
||||
IndexPatternsContract,
|
||||
SavedQuery,
|
||||
UI_SETTINGS,
|
||||
} from '../../../../../src/plugins/data/public';
|
||||
|
||||
interface State {
|
||||
|
@ -83,17 +82,13 @@ export function App({
|
|||
onAppLeave: AppMountParameters['onAppLeave'];
|
||||
history: History;
|
||||
}) {
|
||||
const language =
|
||||
storage.get('kibana.userQueryLanguage') ||
|
||||
core.uiSettings.get(UI_SETTINGS.SEARCH_QUERY_LANGUAGE);
|
||||
|
||||
const [state, setState] = useState<State>(() => {
|
||||
const currentRange = data.query.timefilter.timefilter.getTime();
|
||||
return {
|
||||
isLoading: !!docId,
|
||||
isSaveModalVisible: false,
|
||||
indexPatternsForTopNav: [],
|
||||
query: { query: '', language },
|
||||
query: data.query.queryString.getDefaultQuery(),
|
||||
dateRange: {
|
||||
fromDate: currentRange.from,
|
||||
toDate: currentRange.to,
|
||||
|
@ -473,12 +468,7 @@ export function App({
|
|||
...s,
|
||||
savedQuery: undefined,
|
||||
filters: data.query.filterManager.getGlobalFilters(),
|
||||
query: {
|
||||
query: '',
|
||||
language:
|
||||
storage.get('kibana.userQueryLanguage') ||
|
||||
core.uiSettings.get(UI_SETTINGS.SEARCH_QUERY_LANGUAGE),
|
||||
},
|
||||
query: data.query.queryString.getDefaultQuery(),
|
||||
}));
|
||||
}}
|
||||
query={state.query}
|
||||
|
|
|
@ -20,8 +20,7 @@ import {
|
|||
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { UI_SETTINGS } from '../../../../../../../src/plugins/data/public';
|
||||
import { getIndexPatternService, getUiSettings, getData } from '../../../kibana_services';
|
||||
import { getIndexPatternService, getData } from '../../../kibana_services';
|
||||
import { GlobalFilterCheckbox } from '../../../components/global_filter_checkbox';
|
||||
|
||||
export class FilterEditor extends Component {
|
||||
|
@ -82,7 +81,6 @@ export class FilterEditor extends Component {
|
|||
|
||||
_renderQueryPopover() {
|
||||
const layerQuery = this.props.layer.getQuery();
|
||||
const uiSettings = getUiSettings();
|
||||
const { SearchBar } = getData().ui;
|
||||
|
||||
return (
|
||||
|
@ -99,11 +97,7 @@ export class FilterEditor extends Component {
|
|||
showFilterBar={false}
|
||||
showDatePicker={false}
|
||||
showQueryInput={true}
|
||||
query={
|
||||
layerQuery
|
||||
? layerQuery
|
||||
: { language: uiSettings.get(UI_SETTINGS.SEARCH_QUERY_LANGUAGE), query: '' }
|
||||
}
|
||||
query={layerQuery ? layerQuery : getData().query.queryString.getDefaultQuery()}
|
||||
onQuerySubmit={this._onQueryChange}
|
||||
indexPatterns={this.state.indexPatterns}
|
||||
customSubmitButton={
|
||||
|
|
|
@ -8,8 +8,7 @@ import React, { Component } from 'react';
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { EuiButton, EuiPopover, EuiExpression, EuiFormHelpText } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { UI_SETTINGS } from '../../../../../../../../src/plugins/data/public';
|
||||
import { getUiSettings, getData } from '../../../../kibana_services';
|
||||
import { getData } from '../../../../kibana_services';
|
||||
|
||||
export class WhereExpression extends Component {
|
||||
state = {
|
||||
|
@ -77,11 +76,7 @@ export class WhereExpression extends Component {
|
|||
showFilterBar={false}
|
||||
showDatePicker={false}
|
||||
showQueryInput={true}
|
||||
query={
|
||||
whereQuery
|
||||
? whereQuery
|
||||
: { language: getUiSettings().get(UI_SETTINGS.SEARCH_QUERY_LANGUAGE), query: '' }
|
||||
}
|
||||
query={whereQuery ? whereQuery : getData().query.queryString.getDefaultQuery()}
|
||||
onQuerySubmit={this._onQueryChange}
|
||||
indexPatterns={[indexPattern]}
|
||||
customSubmitButton={
|
||||
|
|
|
@ -4,12 +4,9 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { getUiSettings } from '../../kibana_services';
|
||||
import { UI_SETTINGS } from '../../../../../../src/plugins/data/public';
|
||||
|
||||
export function getInitialQuery({ mapStateJSON, appState = {}, userQueryLanguage }) {
|
||||
const settings = getUiSettings();
|
||||
import { getData } from '../../kibana_services';
|
||||
|
||||
export function getInitialQuery({ mapStateJSON, appState = {} }) {
|
||||
if (appState.query) {
|
||||
return appState.query;
|
||||
}
|
||||
|
@ -21,8 +18,5 @@ export function getInitialQuery({ mapStateJSON, appState = {}, userQueryLanguage
|
|||
}
|
||||
}
|
||||
|
||||
return {
|
||||
query: '',
|
||||
language: userQueryLanguage || settings.get(UI_SETTINGS.SEARCH_QUERY_LANGUAGE),
|
||||
};
|
||||
return getData().query.queryString.getDefaultQuery();
|
||||
}
|
||||
|
|
|
@ -14,7 +14,6 @@ import {
|
|||
getToasts,
|
||||
getCoreI18n,
|
||||
getData,
|
||||
getUiSettings,
|
||||
} from '../../../kibana_services';
|
||||
import {
|
||||
SavedObjectSaveModal,
|
||||
|
@ -46,16 +45,13 @@ export function MapsTopNavMenu({
|
|||
isOpenSettingsDisabled,
|
||||
}) {
|
||||
const { TopNavMenu } = getNavigation().ui;
|
||||
const { filterManager } = getData().query;
|
||||
const { filterManager, queryString } = getData().query;
|
||||
const showSaveQuery = getMapsCapabilities().saveQuery;
|
||||
const onClearSavedQuery = () => {
|
||||
onQuerySaved(undefined);
|
||||
onQueryChange({
|
||||
filters: filterManager.getGlobalFilters(),
|
||||
query: {
|
||||
query: '',
|
||||
language: getUiSettings().get('search:queryLanguage'),
|
||||
},
|
||||
query: queryString.getDefaultQuery(),
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -13,7 +13,6 @@ import {
|
|||
getIndexPatternService,
|
||||
getToasts,
|
||||
getData,
|
||||
getUiSettings,
|
||||
getCoreChrome,
|
||||
} from '../../../kibana_services';
|
||||
import { copyPersistentState } from '../../../reducers/util';
|
||||
|
@ -274,6 +273,7 @@ export class MapsAppView extends React.Component {
|
|||
|
||||
_initQueryTimeRefresh() {
|
||||
const { setRefreshConfig, savedMap } = this.props;
|
||||
const { queryString } = getData().query;
|
||||
// TODO: Handle null when converting to TS
|
||||
const globalState = getGlobalState();
|
||||
const mapStateJSON = savedMap ? savedMap.mapStateJSON : undefined;
|
||||
|
@ -281,7 +281,6 @@ export class MapsAppView extends React.Component {
|
|||
query: getInitialQuery({
|
||||
mapStateJSON,
|
||||
appState: this._appStateManager.getAppState(),
|
||||
userQueryLanguage: getUiSettings().get('search:queryLanguage'),
|
||||
}),
|
||||
time: getInitialTimeFilters({
|
||||
mapStateJSON,
|
||||
|
@ -292,6 +291,8 @@ export class MapsAppView extends React.Component {
|
|||
globalState,
|
||||
}),
|
||||
};
|
||||
|
||||
if (newState.query) queryString.setQuery(newState.query);
|
||||
this.setState({ query: newState.query, time: newState.time });
|
||||
updateGlobalState(
|
||||
{
|
||||
|
|
|
@ -31,6 +31,7 @@ export function useAppStateSyncing(appStateManager) {
|
|||
};
|
||||
const stopSyncingQueryAppStateWithStateContainer = connectToQueryState(query, stateContainer, {
|
||||
filters: esFilters.FilterStateStore.APP_STATE,
|
||||
query: true,
|
||||
});
|
||||
|
||||
// sets up syncing app state container with url
|
||||
|
|
Loading…
Reference in a new issue