mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
making vis (completely) serializable (#64207)
This commit is contained in:
parent
3f43763bc3
commit
15c0644c97
50 changed files with 572 additions and 402 deletions
|
@ -0,0 +1,13 @@
|
|||
<!-- 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) > [extractSearchSourceReferences](./kibana-plugin-plugins-data-public.extractsearchsourcereferences.md)
|
||||
|
||||
## extractSearchSourceReferences variable
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
extractReferences: (state: SearchSourceFields) => [SearchSourceFields & {
|
||||
indexRefName?: string | undefined;
|
||||
}, SavedObjectReference[]]
|
||||
```
|
|
@ -0,0 +1,13 @@
|
|||
<!-- 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) > [injectSearchSourceReferences](./kibana-plugin-plugins-data-public.injectsearchsourcereferences.md)
|
||||
|
||||
## injectSearchSourceReferences variable
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
injectReferences: (searchSourceFields: SearchSourceFields & {
|
||||
indexRefName: string;
|
||||
}, references: SavedObjectReference[]) => SearchSourceFields
|
||||
```
|
|
@ -101,11 +101,14 @@
|
|||
| [esFilters](./kibana-plugin-plugins-data-public.esfilters.md) | |
|
||||
| [esKuery](./kibana-plugin-plugins-data-public.eskuery.md) | |
|
||||
| [esQuery](./kibana-plugin-plugins-data-public.esquery.md) | |
|
||||
| [extractSearchSourceReferences](./kibana-plugin-plugins-data-public.extractsearchsourcereferences.md) | |
|
||||
| [fieldFormats](./kibana-plugin-plugins-data-public.fieldformats.md) | |
|
||||
| [FilterBar](./kibana-plugin-plugins-data-public.filterbar.md) | |
|
||||
| [getIndexPatternFieldListCreator](./kibana-plugin-plugins-data-public.getindexpatternfieldlistcreator.md) | |
|
||||
| [getKbnTypeNames](./kibana-plugin-plugins-data-public.getkbntypenames.md) | Get the esTypes known by all kbnFieldTypes {<!-- -->Array<string>} |
|
||||
| [indexPatterns](./kibana-plugin-plugins-data-public.indexpatterns.md) | |
|
||||
| [injectSearchSourceReferences](./kibana-plugin-plugins-data-public.injectsearchsourcereferences.md) | |
|
||||
| [parseSearchSourceJSON](./kibana-plugin-plugins-data-public.parsesearchsourcejson.md) | |
|
||||
| [QueryStringInput](./kibana-plugin-plugins-data-public.querystringinput.md) | |
|
||||
| [search](./kibana-plugin-plugins-data-public.search.md) | |
|
||||
| [SearchBar](./kibana-plugin-plugins-data-public.searchbar.md) | |
|
||||
|
|
|
@ -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) > [parseSearchSourceJSON](./kibana-plugin-plugins-data-public.parsesearchsourcejson.md)
|
||||
|
||||
## parseSearchSourceJSON variable
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
parseSearchSourceJSON: (searchSourceJSON: string) => SearchSourceFields
|
||||
```
|
|
@ -32,6 +32,7 @@ import { chartPluginMock } from '../../../../../plugins/charts/public/mocks';
|
|||
import { advancedSettingsMock } from '../../../../../plugins/advanced_settings/public/mocks';
|
||||
import { savedObjectsManagementPluginMock } from '../../../../../plugins/saved_objects_management/public/mocks';
|
||||
import { visualizationsPluginMock } from '../../../../../plugins/visualizations/public/mocks';
|
||||
import { discoverPluginMock } from '../../../../../plugins/discover/public/mocks';
|
||||
/* eslint-enable @kbn/eslint/no-restricted-paths */
|
||||
|
||||
export const pluginsMock = {
|
||||
|
@ -48,6 +49,7 @@ export const pluginsMock = {
|
|||
visualizations: visualizationsPluginMock.createSetupContract(),
|
||||
kibanaLegacy: kibanaLegacyPluginMock.createSetupContract(),
|
||||
savedObjectsManagement: savedObjectsManagementPluginMock.createSetupContract(),
|
||||
discover: discoverPluginMock.createSetupContract(),
|
||||
}),
|
||||
createStart: () => ({
|
||||
data: dataPluginMock.createStartContract(),
|
||||
|
@ -62,6 +64,7 @@ export const pluginsMock = {
|
|||
visualizations: visualizationsPluginMock.createStartContract(),
|
||||
kibanaLegacy: kibanaLegacyPluginMock.createStartContract(),
|
||||
savedObjectsManagement: savedObjectsManagementPluginMock.createStartContract(),
|
||||
discover: discoverPluginMock.createStartContract(),
|
||||
}),
|
||||
};
|
||||
|
||||
|
|
|
@ -515,6 +515,7 @@ export const npStart = {
|
|||
docViews: {
|
||||
DocViewer: () => null,
|
||||
},
|
||||
savedSearchLoader: {},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -57,6 +57,7 @@ export function setStartServices(npStart: NpStart) {
|
|||
dataServices.setIndexPatterns(npStart.plugins.data.indexPatterns);
|
||||
dataServices.setQueryService(npStart.plugins.data.query);
|
||||
dataServices.setSearchService(npStart.plugins.data.search);
|
||||
|
||||
visualizationsServices.setI18n(npStart.core.i18n);
|
||||
visualizationsServices.setTypes(
|
||||
pick(npStart.plugins.visualizations, ['get', 'all', 'getAliases'])
|
||||
|
@ -82,4 +83,5 @@ export function setStartServices(npStart: NpStart) {
|
|||
visualizationTypes: visualizationsServices.getTypes(),
|
||||
});
|
||||
visualizationsServices.setSavedVisualizationsLoader(savedVisualizationsLoader);
|
||||
visualizationsServices.setSavedSearchLoader(npStart.plugins.discover.savedSearchLoader);
|
||||
}
|
||||
|
|
|
@ -358,6 +358,9 @@ export {
|
|||
SearchResponse,
|
||||
SearchError,
|
||||
ISearchSource,
|
||||
parseSearchSourceJSON,
|
||||
injectSearchSourceReferences,
|
||||
extractSearchSourceReferences,
|
||||
SearchSourceFields,
|
||||
EsQuerySortValue,
|
||||
SortDirection,
|
||||
|
|
|
@ -46,7 +46,6 @@ import * as React_2 from 'react';
|
|||
import { Required } from '@kbn/utility-types';
|
||||
import * as Rx from 'rxjs';
|
||||
import { SavedObject as SavedObject_2 } from 'src/core/public';
|
||||
import { SavedObjectReference } from 'kibana/public';
|
||||
import { SavedObjectsClientContract } from 'src/core/public';
|
||||
import { SearchParams } from 'elasticsearch';
|
||||
import { SearchResponse as SearchResponse_2 } from 'elasticsearch';
|
||||
|
@ -420,6 +419,14 @@ export type ExistsFilter = Filter & {
|
|||
exists?: FilterExistsProperty;
|
||||
};
|
||||
|
||||
// Warning: (ae-forgotten-export) The symbol "SavedObjectReference" needs to be exported by the entry point index.d.ts
|
||||
// Warning: (ae-missing-release-tag) "extractReferences" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export const extractSearchSourceReferences: (state: SearchSourceFields) => [SearchSourceFields & {
|
||||
indexRefName?: string | undefined;
|
||||
}, SavedObjectReference[]];
|
||||
|
||||
// Warning: (ae-missing-release-tag) "FetchOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
|
@ -1059,6 +1066,13 @@ export interface IndexPatternTypeMeta {
|
|||
aggs?: Record<string, IndexPatternAggRestrictions>;
|
||||
}
|
||||
|
||||
// Warning: (ae-missing-release-tag) "injectReferences" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export const injectSearchSourceReferences: (searchSourceFields: SearchSourceFields & {
|
||||
indexRefName: string;
|
||||
}, references: SavedObjectReference[]) => SearchSourceFields;
|
||||
|
||||
// Warning: (ae-missing-release-tag) "InputTimeRange" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
|
@ -1273,6 +1287,11 @@ export interface OptionedValueProp {
|
|||
// @public (undocumented)
|
||||
export type ParsedInterval = ReturnType<typeof parseEsInterval>;
|
||||
|
||||
// Warning: (ae-missing-release-tag) "parseSearchSourceJSON" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export const parseSearchSourceJSON: (searchSourceJSON: string) => SearchSourceFields;
|
||||
|
||||
// Warning: (ae-missing-release-tag) "PhraseFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
|
@ -1809,20 +1828,20 @@ export type TSearchStrategyProvider<T extends TStrategyTypes> = (context: ISearc
|
|||
// src/plugins/data/public/index.ts:237:27 - (ae-forgotten-export) The symbol "getFromSavedObject" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:237:27 - (ae-forgotten-export) The symbol "flattenHitWrapper" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:237:27 - (ae-forgotten-export) The symbol "formatHitProvider" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:374:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:374:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:374:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:374:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:376:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:377:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:386:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:387:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:388:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:392:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:393:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:396:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:397:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:400:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:377:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:377:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:377:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:377:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:379:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:380:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:389:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:390:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:391:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:395:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:396:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:399:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:400:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:403: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:33:33 - (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:37:1 - (ae-forgotten-export) The symbol "QueryStateChange" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/types.ts:52:5 - (ae-forgotten-export) The symbol "createFiltersFromValueClickAction" needs to be exported by the entry point index.d.ts
|
||||
|
|
|
@ -282,7 +282,7 @@ export const esaggs = (): ExpressionFunctionDefinition<typeof name, Input, Argum
|
|||
const aggs = searchService.aggs.createAggConfigs(indexPattern, aggConfigsState);
|
||||
|
||||
// we should move searchSource creation inside courier request handler
|
||||
const searchSource = searchService.searchSource.create();
|
||||
const searchSource = await searchService.searchSource.create();
|
||||
|
||||
searchSource.setField('index', indexPattern);
|
||||
searchSource.setField('size', 0);
|
||||
|
|
|
@ -59,6 +59,9 @@ export {
|
|||
SearchSourceFields,
|
||||
EsQuerySortValue,
|
||||
SortDirection,
|
||||
extractReferences as extractSearchSourceReferences,
|
||||
injectReferences as injectSearchSourceReferences,
|
||||
parseSearchSourceJSON,
|
||||
} from './search_source';
|
||||
|
||||
export { SearchInterceptor } from './search_interceptor';
|
||||
|
|
|
@ -21,12 +21,7 @@ import { Plugin, CoreSetup, CoreStart, PackageInfo } from '../../../../core/publ
|
|||
import { ExpressionsSetup } from '../../../../plugins/expressions/public';
|
||||
|
||||
import { SYNC_SEARCH_STRATEGY, syncSearchStrategyProvider } from './sync_search_strategy';
|
||||
import {
|
||||
createSearchSourceFromJSON,
|
||||
SearchSource,
|
||||
SearchSourceDependencies,
|
||||
SearchSourceFields,
|
||||
} from './search_source';
|
||||
import { createSearchSource, SearchSource, SearchSourceDependencies } from './search_source';
|
||||
import { ISearchSetup, ISearchStart, TSearchStrategyProvider, TSearchStrategiesMap } from './types';
|
||||
import { TStrategyTypes } from './strategy_types';
|
||||
import { getEsClient, LegacyApiCaller } from './legacy';
|
||||
|
@ -171,8 +166,10 @@ export class SearchService implements Plugin<ISearchSetup, ISearchStart> {
|
|||
},
|
||||
search,
|
||||
searchSource: {
|
||||
create: (fields?: SearchSourceFields) => new SearchSource(fields, searchSourceDependencies),
|
||||
fromJSON: createSearchSourceFromJSON(dependencies.indexPatterns, searchSourceDependencies),
|
||||
create: createSearchSource(dependencies.indexPatterns, searchSourceDependencies),
|
||||
createEmpty: () => {
|
||||
return new SearchSource({}, searchSourceDependencies);
|
||||
},
|
||||
},
|
||||
setInterceptor: (searchInterceptor: SearchInterceptor) => {
|
||||
// TODO: should an intercepror have a destroy method?
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { createSearchSourceFromJSON } from './create_search_source';
|
||||
import { createSearchSource as createSearchSourceFactory } from './create_search_source';
|
||||
import { IIndexPattern } from '../../../common/index_patterns';
|
||||
import { IndexPatternsContract } from '../../index_patterns/index_patterns';
|
||||
import { Filter } from '../../../common/es_query/filters';
|
||||
|
@ -27,7 +27,7 @@ describe('createSearchSource', () => {
|
|||
const indexPatternMock: IIndexPattern = {} as IIndexPattern;
|
||||
let indexPatternContractMock: jest.Mocked<IndexPatternsContract>;
|
||||
let dependencies: any;
|
||||
let createSearchSource: ReturnType<typeof createSearchSourceFromJSON>;
|
||||
let createSearchSource: ReturnType<typeof createSearchSourceFactory>;
|
||||
|
||||
beforeEach(() => {
|
||||
const core = coreMock.createStart();
|
||||
|
@ -43,27 +43,17 @@ describe('createSearchSource', () => {
|
|||
get: jest.fn().mockReturnValue(Promise.resolve(indexPatternMock)),
|
||||
} as unknown) as jest.Mocked<IndexPatternsContract>;
|
||||
|
||||
createSearchSource = createSearchSourceFromJSON(indexPatternContractMock, dependencies);
|
||||
createSearchSource = createSearchSourceFactory(indexPatternContractMock, dependencies);
|
||||
});
|
||||
|
||||
test('should fail if JSON is invalid', () => {
|
||||
expect(createSearchSource('{', [])).rejects.toThrow();
|
||||
expect(createSearchSource('0', [])).rejects.toThrow();
|
||||
expect(createSearchSource('"abcdefg"', [])).rejects.toThrow();
|
||||
});
|
||||
|
||||
test('should set fields', async () => {
|
||||
const searchSource = await createSearchSource(
|
||||
JSON.stringify({
|
||||
highlightAll: true,
|
||||
query: {
|
||||
query: '',
|
||||
language: 'kuery',
|
||||
},
|
||||
}),
|
||||
[]
|
||||
);
|
||||
|
||||
it('should set fields', async () => {
|
||||
const searchSource = await createSearchSource({
|
||||
highlightAll: true,
|
||||
query: {
|
||||
query: '',
|
||||
language: 'kuery',
|
||||
},
|
||||
});
|
||||
expect(searchSource.getOwnField('highlightAll')).toBe(true);
|
||||
expect(searchSource.getOwnField('query')).toEqual({
|
||||
query: '',
|
||||
|
@ -71,66 +61,32 @@ describe('createSearchSource', () => {
|
|||
});
|
||||
});
|
||||
|
||||
test('should resolve referenced index pattern', async () => {
|
||||
const searchSource = await createSearchSource(
|
||||
JSON.stringify({
|
||||
indexRefName: 'kibanaSavedObjectMeta.searchSourceJSON.index',
|
||||
}),
|
||||
[
|
||||
it('should set filters and resolve referenced index patterns', async () => {
|
||||
const searchSource = await createSearchSource({
|
||||
filter: [
|
||||
{
|
||||
id: '123-456',
|
||||
type: 'index-pattern',
|
||||
name: 'kibanaSavedObjectMeta.searchSourceJSON.index',
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
expect(indexPatternContractMock.get).toHaveBeenCalledWith('123-456');
|
||||
expect(searchSource.getOwnField('index')).toBe(indexPatternMock);
|
||||
});
|
||||
|
||||
test('should set filters and resolve referenced index patterns', async () => {
|
||||
const searchSource = await createSearchSource(
|
||||
JSON.stringify({
|
||||
filter: [
|
||||
{
|
||||
meta: {
|
||||
alias: null,
|
||||
negate: false,
|
||||
disabled: false,
|
||||
type: 'phrase',
|
||||
key: 'category.keyword',
|
||||
params: {
|
||||
query: "Men's Clothing",
|
||||
},
|
||||
indexRefName: 'kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index',
|
||||
meta: {
|
||||
alias: null,
|
||||
negate: false,
|
||||
disabled: false,
|
||||
type: 'phrase',
|
||||
key: 'category.keyword',
|
||||
params: {
|
||||
query: "Men's Clothing",
|
||||
},
|
||||
query: {
|
||||
match_phrase: {
|
||||
'category.keyword': "Men's Clothing",
|
||||
},
|
||||
},
|
||||
$state: {
|
||||
store: 'appState',
|
||||
index: '123-456',
|
||||
},
|
||||
query: {
|
||||
match_phrase: {
|
||||
'category.keyword': "Men's Clothing",
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
[
|
||||
{
|
||||
id: '123-456',
|
||||
type: 'index-pattern',
|
||||
name: 'kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index',
|
||||
},
|
||||
]
|
||||
);
|
||||
],
|
||||
});
|
||||
const filters = searchSource.getOwnField('filter') as Filter[];
|
||||
|
||||
expect(filters[0]).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"$state": Object {
|
||||
"store": "appState",
|
||||
},
|
||||
"meta": Object {
|
||||
"alias": null,
|
||||
"disabled": false,
|
||||
|
@ -151,15 +107,11 @@ describe('createSearchSource', () => {
|
|||
`);
|
||||
});
|
||||
|
||||
test('should migrate legacy queries on the fly', async () => {
|
||||
const searchSource = await createSearchSource(
|
||||
JSON.stringify({
|
||||
highlightAll: true,
|
||||
query: 'a:b',
|
||||
}),
|
||||
[]
|
||||
);
|
||||
|
||||
it('should migrate legacy queries on the fly', async () => {
|
||||
const searchSource = await createSearchSource({
|
||||
highlightAll: true,
|
||||
query: 'a:b' as any,
|
||||
});
|
||||
expect(searchSource.getOwnField('query')).toEqual({
|
||||
query: 'a:b',
|
||||
language: 'lucene',
|
||||
|
|
|
@ -16,11 +16,8 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { transform, defaults, isFunction } from 'lodash';
|
||||
import { SavedObjectReference } from 'kibana/public';
|
||||
import { migrateLegacyQuery } from '../../../../kibana_legacy/public';
|
||||
import { InvalidJSONProperty } from '../../../../kibana_utils/public';
|
||||
import { SearchSourceDependencies, SearchSource, ISearchSource } from './search_source';
|
||||
import { SearchSource, SearchSourceDependencies } from './search_source';
|
||||
import { IndexPatternsContract } from '../../index_patterns/index_patterns';
|
||||
import { SearchSourceFields } from './types';
|
||||
|
||||
|
@ -33,6 +30,7 @@ import { SearchSourceFields } from './types';
|
|||
* the start contract of the data plugin as part of the search service
|
||||
*
|
||||
* @param indexPatterns The index patterns contract of the data plugin
|
||||
* @param searchSourceDependencies
|
||||
*
|
||||
* @return Wired utility function taking two parameters `searchSourceJson`, the json string
|
||||
* returned by `serializeSearchSource` and `references`, a list of references including the ones
|
||||
|
@ -40,73 +38,20 @@ import { SearchSourceFields } from './types';
|
|||
*
|
||||
*
|
||||
* @public */
|
||||
export const createSearchSourceFromJSON = (
|
||||
export const createSearchSource = (
|
||||
indexPatterns: IndexPatternsContract,
|
||||
searchSourceDependencies: SearchSourceDependencies
|
||||
) => async (
|
||||
searchSourceJson: string,
|
||||
references: SavedObjectReference[]
|
||||
): Promise<ISearchSource> => {
|
||||
const searchSource = new SearchSource({}, searchSourceDependencies);
|
||||
) => async (searchSourceFields: SearchSourceFields = {}) => {
|
||||
const fields = { ...searchSourceFields };
|
||||
|
||||
// if we have a searchSource, set its values based on the searchSourceJson field
|
||||
let searchSourceValues: Record<string, unknown>;
|
||||
try {
|
||||
searchSourceValues = JSON.parse(searchSourceJson);
|
||||
} catch (e) {
|
||||
throw new InvalidJSONProperty(
|
||||
`Invalid JSON in search source. ${e.message} JSON: ${searchSourceJson}`
|
||||
);
|
||||
// hydrating index pattern
|
||||
if (fields.index && typeof fields.index === 'string') {
|
||||
fields.index = await indexPatterns.get(searchSourceFields.index as any);
|
||||
}
|
||||
|
||||
// This detects a scenario where documents with invalid JSON properties have been imported into the saved object index.
|
||||
// (This happened in issue #20308)
|
||||
if (!searchSourceValues || typeof searchSourceValues !== 'object') {
|
||||
throw new InvalidJSONProperty('Invalid JSON in search source.');
|
||||
}
|
||||
const searchSource = new SearchSource(fields, searchSourceDependencies);
|
||||
|
||||
// Inject index id if a reference is saved
|
||||
if (searchSourceValues.indexRefName) {
|
||||
const reference = references.find(ref => ref.name === searchSourceValues.indexRefName);
|
||||
if (!reference) {
|
||||
throw new Error(`Could not find reference for ${searchSourceValues.indexRefName}`);
|
||||
}
|
||||
searchSourceValues.index = reference.id;
|
||||
delete searchSourceValues.indexRefName;
|
||||
}
|
||||
|
||||
if (searchSourceValues.filter && Array.isArray(searchSourceValues.filter)) {
|
||||
searchSourceValues.filter.forEach((filterRow: any) => {
|
||||
if (!filterRow.meta || !filterRow.meta.indexRefName) {
|
||||
return;
|
||||
}
|
||||
const reference = references.find((ref: any) => ref.name === filterRow.meta.indexRefName);
|
||||
if (!reference) {
|
||||
throw new Error(`Could not find reference for ${filterRow.meta.indexRefName}`);
|
||||
}
|
||||
filterRow.meta.index = reference.id;
|
||||
delete filterRow.meta.indexRefName;
|
||||
});
|
||||
}
|
||||
|
||||
if (searchSourceValues.index && typeof searchSourceValues.index === 'string') {
|
||||
searchSourceValues.index = await indexPatterns.get(searchSourceValues.index);
|
||||
}
|
||||
|
||||
const searchSourceFields = searchSource.getFields();
|
||||
const fnProps = transform(
|
||||
searchSourceFields,
|
||||
function(dynamic, val, name) {
|
||||
if (isFunction(val) && name) dynamic[name] = val;
|
||||
},
|
||||
{}
|
||||
);
|
||||
|
||||
// This assignment might hide problems because the type of values passed from the parsed JSON
|
||||
// might not fit the SearchSourceFields interface.
|
||||
const newFields: SearchSourceFields = defaults(searchSourceValues, fnProps);
|
||||
|
||||
searchSource.setFields(newFields);
|
||||
// todo: move to migration script .. create issue
|
||||
const query = searchSource.getOwnField('query');
|
||||
|
||||
if (typeof query !== 'undefined') {
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* 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 { SavedObjectReference } from '../../../../../core/types';
|
||||
import { Filter } from '../../../common/es_query/filters';
|
||||
import { SearchSourceFields } from './types';
|
||||
|
||||
export const extractReferences = (
|
||||
state: SearchSourceFields
|
||||
): [SearchSourceFields & { indexRefName?: string }, SavedObjectReference[]] => {
|
||||
let searchSourceFields: SearchSourceFields & { indexRefName?: string } = { ...state };
|
||||
const references: SavedObjectReference[] = [];
|
||||
if (searchSourceFields.index) {
|
||||
const indexId = searchSourceFields.index.id || ((searchSourceFields.index as any) as string);
|
||||
const refName = 'kibanaSavedObjectMeta.searchSourceJSON.index';
|
||||
references.push({
|
||||
name: refName,
|
||||
type: 'index-pattern',
|
||||
id: indexId,
|
||||
});
|
||||
searchSourceFields = {
|
||||
...searchSourceFields,
|
||||
indexRefName: refName,
|
||||
index: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
if (searchSourceFields.filter) {
|
||||
searchSourceFields = {
|
||||
...searchSourceFields,
|
||||
filter: (searchSourceFields.filter as Filter[]).map((filterRow, i) => {
|
||||
if (!filterRow.meta || !filterRow.meta.index) {
|
||||
return filterRow;
|
||||
}
|
||||
const refName = `kibanaSavedObjectMeta.searchSourceJSON.filter[${i}].meta.index`;
|
||||
references.push({
|
||||
name: refName,
|
||||
type: 'index-pattern',
|
||||
id: filterRow.meta.index,
|
||||
});
|
||||
return {
|
||||
...filterRow,
|
||||
meta: {
|
||||
...filterRow.meta,
|
||||
indexRefName: refName,
|
||||
index: undefined,
|
||||
},
|
||||
};
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
return [searchSourceFields, references];
|
||||
};
|
|
@ -18,5 +18,8 @@
|
|||
*/
|
||||
|
||||
export { SearchSource, ISearchSource, SearchSourceDependencies } from './search_source';
|
||||
export { createSearchSourceFromJSON } from './create_search_source';
|
||||
export { createSearchSource } from './create_search_source';
|
||||
export { SortDirection, EsQuerySortValue, SearchSourceFields } from './types';
|
||||
export { injectReferences } from './inject_references';
|
||||
export { extractReferences } from './extract_references';
|
||||
export { parseSearchSourceJSON } from './parse_json';
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* 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 { SearchSourceFields } from './types';
|
||||
import { SavedObjectReference } from '../../../../../core/types';
|
||||
|
||||
export const injectReferences = (
|
||||
searchSourceFields: SearchSourceFields & { indexRefName: string },
|
||||
references: SavedObjectReference[]
|
||||
) => {
|
||||
const searchSourceReturnFields: SearchSourceFields = { ...searchSourceFields };
|
||||
// Inject index id if a reference is saved
|
||||
if (searchSourceFields.indexRefName) {
|
||||
const reference = references.find(ref => ref.name === searchSourceFields.indexRefName);
|
||||
if (!reference) {
|
||||
throw new Error(`Could not find reference for ${searchSourceFields.indexRefName}`);
|
||||
}
|
||||
// @ts-ignore
|
||||
searchSourceReturnFields.index = reference.id;
|
||||
// @ts-ignore
|
||||
delete searchSourceReturnFields.indexRefName;
|
||||
}
|
||||
|
||||
if (searchSourceReturnFields.filter && Array.isArray(searchSourceReturnFields.filter)) {
|
||||
searchSourceReturnFields.filter.forEach((filterRow: any) => {
|
||||
if (!filterRow.meta || !filterRow.meta.indexRefName) {
|
||||
return;
|
||||
}
|
||||
const reference = references.find((ref: any) => ref.name === filterRow.meta.indexRefName);
|
||||
if (!reference) {
|
||||
throw new Error(`Could not find reference for ${filterRow.meta.indexRefName}`);
|
||||
}
|
||||
filterRow.meta.index = reference.id;
|
||||
delete filterRow.meta.indexRefName;
|
||||
});
|
||||
}
|
||||
|
||||
return searchSourceReturnFields;
|
||||
};
|
|
@ -43,12 +43,13 @@ export const searchSourceInstanceMock: MockedKeys<ISearchSource> = {
|
|||
getSearchRequestBody: jest.fn(),
|
||||
destroy: jest.fn(),
|
||||
history: [],
|
||||
getSerializedFields: jest.fn(),
|
||||
serialize: jest.fn(),
|
||||
};
|
||||
|
||||
export const searchSourceMock = {
|
||||
create: jest.fn().mockReturnValue(searchSourceInstanceMock),
|
||||
fromJSON: jest.fn().mockReturnValue(searchSourceInstanceMock),
|
||||
createEmpty: jest.fn().mockReturnValue(searchSourceInstanceMock),
|
||||
};
|
||||
|
||||
export const createSearchSourceMock = (fields?: SearchSourceFields) =>
|
||||
|
|
41
src/plugins/data/public/search/search_source/parse_json.ts
Normal file
41
src/plugins/data/public/search/search_source/parse_json.ts
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* 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 { SearchSourceFields } from './types';
|
||||
import { InvalidJSONProperty } from '../../../../kibana_utils/public';
|
||||
|
||||
export const parseSearchSourceJSON = (searchSourceJSON: string) => {
|
||||
// if we have a searchSource, set its values based on the searchSourceJson field
|
||||
let searchSourceValues: SearchSourceFields;
|
||||
try {
|
||||
searchSourceValues = JSON.parse(searchSourceJSON);
|
||||
} catch (e) {
|
||||
throw new InvalidJSONProperty(
|
||||
`Invalid JSON in search source. ${e.message} JSON: ${searchSourceJSON}`
|
||||
);
|
||||
}
|
||||
|
||||
// This detects a scenario where documents with invalid JSON properties have been imported into the saved object index.
|
||||
// (This happened in issue #20308)
|
||||
if (!searchSourceValues || typeof searchSourceValues !== 'object') {
|
||||
throw new InvalidJSONProperty('Invalid JSON in search source.');
|
||||
}
|
||||
|
||||
return searchSourceValues;
|
||||
};
|
|
@ -69,9 +69,9 @@
|
|||
* `appSearchSource`.
|
||||
*/
|
||||
|
||||
import { uniqueId, uniq, extend, pick, difference, set, omit, keys, isFunction } from 'lodash';
|
||||
import { uniqueId, uniq, extend, pick, difference, omit, set, keys, isFunction } from 'lodash';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { CoreStart, SavedObjectReference } from 'kibana/public';
|
||||
import { CoreStart } from 'kibana/public';
|
||||
import { normalizeSortRequest } from './normalize_sort_request';
|
||||
import { filterDocvalueFields } from './filter_docvalue_fields';
|
||||
import { fieldWildcardFilter } from '../../../../kibana_utils/public';
|
||||
|
@ -82,6 +82,7 @@ import { FetchOptions, RequestFailure, getSearchParams, handleResponse } from '.
|
|||
import { getEsQueryConfig, buildEsQuery, Filter } from '../../../common';
|
||||
import { getHighlightRequest } from '../../../common/field_formats';
|
||||
import { fetchSoon } from '../legacy';
|
||||
import { extractReferences } from './extract_references';
|
||||
import { ISearchStartLegacy } from '../types';
|
||||
|
||||
export interface SearchSourceDependencies {
|
||||
|
@ -450,6 +451,25 @@ export class SearchSource {
|
|||
return searchRequest;
|
||||
}
|
||||
|
||||
public getSerializedFields() {
|
||||
const { filter: originalFilters, ...searchSourceFields } = omit(this.getFields(), [
|
||||
'sort',
|
||||
'size',
|
||||
]);
|
||||
let serializedSearchSourceFields: SearchSourceFields = {
|
||||
...searchSourceFields,
|
||||
index: searchSourceFields.index ? searchSourceFields.index.id : undefined,
|
||||
};
|
||||
if (originalFilters) {
|
||||
const filters = this.getFilters(originalFilters);
|
||||
serializedSearchSourceFields = {
|
||||
...serializedSearchSourceFields,
|
||||
filter: filters,
|
||||
};
|
||||
}
|
||||
return serializedSearchSourceFields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes the instance to a JSON string and a set of referenced objects.
|
||||
* Use this method to get a representation of the search source which can be stored in a saved object.
|
||||
|
@ -461,57 +481,8 @@ export class SearchSource {
|
|||
* Using `createSearchSource`, the instance can be re-created.
|
||||
* @public */
|
||||
public serialize() {
|
||||
const references: SavedObjectReference[] = [];
|
||||
|
||||
const {
|
||||
filter: originalFilters,
|
||||
...searchSourceFields
|
||||
}: Omit<SearchSourceFields, 'sort' | 'size'> = omit(this.getFields(), ['sort', 'size']);
|
||||
let serializedSearchSourceFields: Omit<SearchSourceFields, 'sort' | 'size' | 'filter'> & {
|
||||
indexRefName?: string;
|
||||
filter?: Array<Omit<Filter, 'meta'> & { meta: Filter['meta'] & { indexRefName?: string } }>;
|
||||
} = searchSourceFields;
|
||||
if (searchSourceFields.index) {
|
||||
const indexId = searchSourceFields.index.id!;
|
||||
const refName = 'kibanaSavedObjectMeta.searchSourceJSON.index';
|
||||
references.push({
|
||||
name: refName,
|
||||
type: 'index-pattern',
|
||||
id: indexId,
|
||||
});
|
||||
serializedSearchSourceFields = {
|
||||
...serializedSearchSourceFields,
|
||||
indexRefName: refName,
|
||||
index: undefined,
|
||||
};
|
||||
}
|
||||
if (originalFilters) {
|
||||
const filters = this.getFilters(originalFilters);
|
||||
serializedSearchSourceFields = {
|
||||
...serializedSearchSourceFields,
|
||||
filter: filters.map((filterRow, i) => {
|
||||
if (!filterRow.meta || !filterRow.meta.index) {
|
||||
return filterRow;
|
||||
}
|
||||
const refName = `kibanaSavedObjectMeta.searchSourceJSON.filter[${i}].meta.index`;
|
||||
references.push({
|
||||
name: refName,
|
||||
type: 'index-pattern',
|
||||
id: filterRow.meta.index,
|
||||
});
|
||||
return {
|
||||
...filterRow,
|
||||
meta: {
|
||||
...filterRow.meta,
|
||||
indexRefName: refName,
|
||||
index: undefined,
|
||||
},
|
||||
};
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
return { searchSourceJSON: JSON.stringify(serializedSearchSourceFields), references };
|
||||
const [searchSourceFields, references] = extractReferences(this.getSerializedFields());
|
||||
return { searchSourceJSON: JSON.stringify(searchSourceFields), references };
|
||||
}
|
||||
|
||||
private getFilters(filterField: SearchSourceFields['filter']): Filter[] {
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { CoreStart, SavedObjectReference } from 'kibana/public';
|
||||
import { CoreStart } from 'kibana/public';
|
||||
import { SearchAggsSetup, SearchAggsStart } from './aggs';
|
||||
import { ISearch, ISearchGeneric } from './i_search';
|
||||
import { TStrategyTypes } from './strategy_types';
|
||||
|
@ -82,11 +82,8 @@ export interface ISearchStart {
|
|||
setInterceptor: (searchInterceptor: SearchInterceptor) => void;
|
||||
search: ISearchGeneric;
|
||||
searchSource: {
|
||||
create: (fields?: SearchSourceFields) => ISearchSource;
|
||||
fromJSON: (
|
||||
searchSourceJson: string,
|
||||
references: SavedObjectReference[]
|
||||
) => Promise<ISearchSource>;
|
||||
create: (fields?: SearchSourceFields) => Promise<ISearchSource>;
|
||||
createEmpty: () => ISearchSource;
|
||||
};
|
||||
__LEGACY: ISearchStartLegacy;
|
||||
}
|
||||
|
|
|
@ -113,8 +113,8 @@ function fetchContextProvider(indexPatterns: IndexPatternsContract) {
|
|||
async function createSearchSource(indexPattern: IndexPattern, filters: Filter[]) {
|
||||
const { data } = getServices();
|
||||
|
||||
return data.search.searchSource
|
||||
.create()
|
||||
const searchSource = await data.search.searchSource.create();
|
||||
return searchSource
|
||||
.setParent(undefined)
|
||||
.setField('index', indexPattern)
|
||||
.setField('filter', filters);
|
||||
|
|
|
@ -30,7 +30,7 @@ import { MarkdownSimple } from '../../../../../../kibana_react/public';
|
|||
|
||||
export function QueryActionsProvider(Promise) {
|
||||
const { filterManager, indexPatterns, data } = getServices();
|
||||
const fetchAnchor = fetchAnchorProvider(indexPatterns, data.search.searchSource.create());
|
||||
const fetchAnchor = fetchAnchorProvider(indexPatterns, data.search.searchSource.createEmpty());
|
||||
const { fetchSurroundingDocs } = fetchContextProvider(indexPatterns);
|
||||
const { setPredecessorCount, setQueryParameters, setSuccessorCount } = getQueryParameterActions(
|
||||
filterManager,
|
||||
|
|
|
@ -1021,7 +1021,7 @@ function discoverController(
|
|||
},
|
||||
];
|
||||
|
||||
$scope.vis = visualizations.createVis('histogram', {
|
||||
$scope.vis = await visualizations.createVis('histogram', {
|
||||
title: savedSearch.title,
|
||||
params: {
|
||||
addLegend: false,
|
||||
|
@ -1029,8 +1029,7 @@ function discoverController(
|
|||
},
|
||||
data: {
|
||||
aggs: visStateAggs,
|
||||
indexPattern: $scope.searchSource.getField('index').id,
|
||||
searchSource: $scope.searchSource,
|
||||
searchSource: $scope.searchSource.getSerializedFields(),
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -33,9 +33,7 @@ const createSetupContract = (): Setup => {
|
|||
|
||||
const createStartContract = (): Start => {
|
||||
const startContract: Start = {
|
||||
savedSearches: {
|
||||
createLoader: jest.fn(),
|
||||
},
|
||||
savedSearchLoader: {} as any,
|
||||
};
|
||||
return startContract;
|
||||
};
|
||||
|
|
|
@ -39,7 +39,7 @@ import { KibanaLegacySetup, AngularRenderedAppUpdater } from 'src/plugins/kibana
|
|||
import { HomePublicPluginSetup } from 'src/plugins/home/public';
|
||||
import { Start as InspectorPublicPluginStart } from 'src/plugins/inspector/public';
|
||||
import { DataPublicPluginStart, DataPublicPluginSetup, esFilters } from '../../data/public';
|
||||
import { SavedObjectLoader, SavedObjectKibanaServices } from '../../saved_objects/public';
|
||||
import { SavedObjectLoader } from '../../saved_objects/public';
|
||||
import { createKbnUrlTracker } from '../../kibana_utils/public';
|
||||
|
||||
import { DocViewInput, DocViewInputFn } from './application/doc_views/doc_views_types';
|
||||
|
@ -73,13 +73,7 @@ export interface DiscoverSetup {
|
|||
}
|
||||
|
||||
export interface DiscoverStart {
|
||||
savedSearches: {
|
||||
/**
|
||||
* Create a {@link SavedObjectLoader | loader} to handle the saved searches type.
|
||||
* @param services
|
||||
*/
|
||||
createLoader(services: SavedObjectKibanaServices): SavedObjectLoader;
|
||||
};
|
||||
savedSearchLoader: SavedObjectLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -264,9 +258,13 @@ export class DiscoverPlugin
|
|||
};
|
||||
|
||||
return {
|
||||
savedSearches: {
|
||||
createLoader: createSavedSearchesLoader,
|
||||
},
|
||||
savedSearchLoader: createSavedSearchesLoader({
|
||||
savedObjectsClient: core.savedObjects.client,
|
||||
indexPatterns: plugins.data.indexPatterns,
|
||||
search: plugins.data.search,
|
||||
chrome: core.chrome,
|
||||
overlays: core.overlays,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ import {
|
|||
DataPublicPluginStart,
|
||||
} from 'src/plugins/data/public';
|
||||
|
||||
export function createSearchSource(
|
||||
export async function createSearchSource(
|
||||
{ create }: DataPublicPluginStart['search']['searchSource'],
|
||||
initialState: SearchSourceFields | null,
|
||||
indexPattern: IndexPattern,
|
||||
|
@ -34,7 +34,7 @@ export function createSearchSource(
|
|||
filters: PhraseFilter[] = [],
|
||||
timefilter: TimefilterContract
|
||||
) {
|
||||
const searchSource = create(initialState || {});
|
||||
const searchSource = await create(initialState || {});
|
||||
|
||||
// Do not not inherit from rootSearchSource to avoid picking up time and globals
|
||||
searchSource.setParent(undefined);
|
||||
|
|
|
@ -147,7 +147,7 @@ export class ListControl extends Control<PhraseFilterManager> {
|
|||
direction: 'desc',
|
||||
query,
|
||||
});
|
||||
const searchSource = createSearchSource(
|
||||
const searchSource = await createSearchSource(
|
||||
this.searchSource,
|
||||
initialSearchSourceState,
|
||||
indexPattern,
|
||||
|
|
|
@ -84,7 +84,7 @@ export class RangeControl extends Control<RangeFilterManager> {
|
|||
|
||||
const fieldName = this.filterManager.fieldName;
|
||||
const aggs = minMaxAgg(indexPattern.fields.getByName(fieldName));
|
||||
const searchSource = createSearchSource(
|
||||
const searchSource = await createSearchSource(
|
||||
this.searchSource,
|
||||
null,
|
||||
indexPattern,
|
||||
|
|
|
@ -19,7 +19,11 @@
|
|||
import _ from 'lodash';
|
||||
import { EsResponse, SavedObject, SavedObjectConfig, SavedObjectKibanaServices } from '../../types';
|
||||
import { expandShorthand, SavedObjectNotFound } from '../../../../kibana_utils/public';
|
||||
import { IndexPattern } from '../../../../data/public';
|
||||
import {
|
||||
IndexPattern,
|
||||
injectSearchSourceReferences,
|
||||
parseSearchSourceJSON,
|
||||
} from '../../../../data/public';
|
||||
|
||||
/**
|
||||
* A given response of and ElasticSearch containing a plain saved object is applied to the given
|
||||
|
@ -63,12 +67,21 @@ export async function applyESResp(
|
|||
_.assign(savedObject, savedObject._source);
|
||||
savedObject.lastSavedTitle = savedObject.title;
|
||||
|
||||
if (config.searchSource) {
|
||||
if (meta.searchSourceJSON) {
|
||||
try {
|
||||
savedObject.searchSource = await dependencies.search.searchSource.fromJSON(
|
||||
meta.searchSourceJSON,
|
||||
resp.references
|
||||
);
|
||||
let searchSourceValues = parseSearchSourceJSON(meta.searchSourceJSON);
|
||||
|
||||
if (config.searchSource) {
|
||||
searchSourceValues = injectSearchSourceReferences(
|
||||
searchSourceValues as any,
|
||||
resp.references
|
||||
);
|
||||
savedObject.searchSource = await dependencies.search.searchSource.create(
|
||||
searchSourceValues
|
||||
);
|
||||
} else {
|
||||
savedObject.searchSourceFields = searchSourceValues;
|
||||
}
|
||||
} catch (error) {
|
||||
if (
|
||||
error.constructor.name === 'SavedObjectNotFound' &&
|
||||
|
|
|
@ -55,7 +55,7 @@ export function buildSavedObject(
|
|||
savedObject.defaults = config.defaults || {};
|
||||
// optional search source which this object configures
|
||||
savedObject.searchSource = config.searchSource
|
||||
? services.search.searchSource.create()
|
||||
? services.search.searchSource.createEmpty()
|
||||
: undefined;
|
||||
// the id of the document
|
||||
savedObject.id = config.id || void 0;
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
import _ from 'lodash';
|
||||
import { SavedObject, SavedObjectConfig } from '../../types';
|
||||
import { expandShorthand } from '../../../../kibana_utils/public';
|
||||
import { extractSearchSourceReferences } from '../../../../data/public';
|
||||
|
||||
export function serializeSavedObject(savedObject: SavedObject, config: SavedObjectConfig) {
|
||||
// mapping definition for the fields that this object will expose
|
||||
|
@ -48,6 +49,15 @@ export function serializeSavedObject(savedObject: SavedObject, config: SavedObje
|
|||
references.push(...searchSourceReferences);
|
||||
}
|
||||
|
||||
if (savedObject.searchSourceFields) {
|
||||
const [searchSourceFields, searchSourceReferences] = extractSearchSourceReferences(
|
||||
savedObject.searchSourceFields
|
||||
);
|
||||
const searchSourceJSON = JSON.stringify(searchSourceFields);
|
||||
attributes.kibanaSavedObjectMeta = { searchSourceJSON };
|
||||
references.push(...searchSourceReferences);
|
||||
}
|
||||
|
||||
if (savedObject.unresolvedIndexPatternReference) {
|
||||
references.push(savedObject.unresolvedIndexPatternReference);
|
||||
}
|
||||
|
|
|
@ -111,6 +111,7 @@ describe('Saved Object', () => {
|
|||
searchSource: {
|
||||
...dataStartMock.search.searchSource,
|
||||
create: createSearchSourceMock,
|
||||
createEmpty: createSearchSourceMock,
|
||||
},
|
||||
},
|
||||
} as unknown) as SavedObjectKibanaServices);
|
||||
|
@ -572,46 +573,50 @@ describe('Saved Object', () => {
|
|||
});
|
||||
|
||||
it('passes references to search source parsing function', async () => {
|
||||
SavedObjectClass = createSavedObjectClass(({
|
||||
savedObjectsClient: savedObjectsClientStub,
|
||||
indexPatterns: dataStartMock.indexPatterns,
|
||||
search: {
|
||||
...dataStartMock.search,
|
||||
},
|
||||
} as unknown) as SavedObjectKibanaServices);
|
||||
const savedObject = new SavedObjectClass({ type: 'dashboard', searchSource: true });
|
||||
await savedObject.init!();
|
||||
|
||||
const searchSourceJSON = JSON.stringify({
|
||||
indexRefName: 'kibanaSavedObjectMeta.searchSourceJSON.index',
|
||||
filter: [
|
||||
{
|
||||
meta: {
|
||||
indexRefName: 'kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index',
|
||||
return savedObject.init!().then(async () => {
|
||||
const searchSourceJSON = JSON.stringify({
|
||||
indexRefName: 'kibanaSavedObjectMeta.searchSourceJSON.index',
|
||||
filter: [
|
||||
{
|
||||
meta: {
|
||||
indexRefName: 'kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index',
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
const response = {
|
||||
found: true,
|
||||
_source: {
|
||||
kibanaSavedObjectMeta: {
|
||||
searchSourceJSON,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
const response = {
|
||||
found: true,
|
||||
_source: {
|
||||
kibanaSavedObjectMeta: {
|
||||
searchSourceJSON,
|
||||
},
|
||||
},
|
||||
references: [
|
||||
{
|
||||
name: 'kibanaSavedObjectMeta.searchSourceJSON.index',
|
||||
type: 'index-pattern',
|
||||
id: 'my-index-1',
|
||||
},
|
||||
{
|
||||
name: 'kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index',
|
||||
type: 'index-pattern',
|
||||
id: 'my-index-2',
|
||||
},
|
||||
],
|
||||
};
|
||||
const result = await savedObject.applyESResp(response);
|
||||
|
||||
expect(result._source).toEqual({
|
||||
kibanaSavedObjectMeta: {
|
||||
searchSourceJSON:
|
||||
'{"indexRefName":"kibanaSavedObjectMeta.searchSourceJSON.index","filter":[{"meta":{"indexRefName":"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index"}}]}',
|
||||
},
|
||||
references: [
|
||||
{
|
||||
name: 'kibanaSavedObjectMeta.searchSourceJSON.index',
|
||||
type: 'index-pattern',
|
||||
id: 'my-index-1',
|
||||
},
|
||||
{
|
||||
name: 'kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index',
|
||||
type: 'index-pattern',
|
||||
id: 'my-index-2',
|
||||
},
|
||||
],
|
||||
};
|
||||
await savedObject.applyESResp(response);
|
||||
expect(dataStartMock.search.searchSource.create).toBeCalledWith({
|
||||
filter: [{ meta: { index: 'my-index-2' } }],
|
||||
index: 'my-index-1',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -29,6 +29,7 @@ import {
|
|||
IIndexPattern,
|
||||
IndexPatternsContract,
|
||||
ISearchSource,
|
||||
SearchSourceFields,
|
||||
} from '../../data/public';
|
||||
|
||||
export interface SavedObject {
|
||||
|
@ -52,6 +53,7 @@ export interface SavedObject {
|
|||
migrationVersion?: Record<string, any>;
|
||||
save: (saveOptions: SavedObjectSaveOpts) => Promise<string>;
|
||||
searchSource?: ISearchSource;
|
||||
searchSourceFields?: SearchSourceFields;
|
||||
showInRecentlyAccessed: boolean;
|
||||
title: string;
|
||||
unresolvedIndexPatternReference?: SavedObjectReference;
|
||||
|
|
|
@ -21,7 +21,12 @@ import { i18n } from '@kbn/i18n';
|
|||
import { cloneDeep } from 'lodash';
|
||||
import { OverlayStart, SavedObjectReference } from 'src/core/public';
|
||||
import { SavedObject, SavedObjectLoader } from '../../../saved_objects/public';
|
||||
import { IndexPatternsContract, IIndexPattern, DataPublicPluginStart } from '../../../data/public';
|
||||
import {
|
||||
DataPublicPluginStart,
|
||||
IndexPatternsContract,
|
||||
IIndexPattern,
|
||||
injectSearchSourceReferences,
|
||||
} from '../../../data/public';
|
||||
|
||||
type SavedObjectsRawDoc = Record<string, any>;
|
||||
|
||||
|
@ -207,13 +212,17 @@ export async function resolveIndexPatternConflicts(
|
|||
return reference;
|
||||
});
|
||||
|
||||
const serializedSearchSourceWithInjectedReferences = injectSearchSourceReferences(
|
||||
serializedSearchSource,
|
||||
replacedReferences
|
||||
);
|
||||
|
||||
if (!allResolved) {
|
||||
// The user decided to skip this conflict so do nothing
|
||||
return;
|
||||
}
|
||||
obj.searchSource = await dependencies.search.searchSource.fromJSON(
|
||||
JSON.stringify(serializedSearchSource),
|
||||
replacedReferences
|
||||
obj.searchSource = await dependencies.search.searchSource.create(
|
||||
serializedSearchSourceWithInjectedReferences
|
||||
);
|
||||
if (await saveObject(obj, overwriteAll)) {
|
||||
importCount++;
|
||||
|
|
|
@ -280,7 +280,7 @@ exports[`SavedObjectsTable import should show the flyout 1`] = `
|
|||
"search": [MockFunction],
|
||||
"searchSource": Object {
|
||||
"create": [MockFunction],
|
||||
"fromJSON": [MockFunction],
|
||||
"createEmpty": [MockFunction],
|
||||
},
|
||||
"setInterceptor": [MockFunction],
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ export const registerServices = async (
|
|||
registry: ISavedObjectsManagementServiceRegistry,
|
||||
getStartServices: StartServicesAccessor<StartDependencies, SavedObjectsManagementPluginStart>
|
||||
) => {
|
||||
const [coreStart, { dashboard, data, visualizations, discover }] = await getStartServices();
|
||||
const [, { dashboard, visualizations, discover }] = await getStartServices();
|
||||
|
||||
if (dashboard) {
|
||||
registry.register({
|
||||
|
@ -47,13 +47,7 @@ export const registerServices = async (
|
|||
registry.register({
|
||||
id: 'savedSearches',
|
||||
title: 'searches',
|
||||
service: discover.savedSearches.createLoader({
|
||||
savedObjectsClient: coreStart.savedObjects.client,
|
||||
indexPatterns: data.indexPatterns,
|
||||
search: data.search,
|
||||
chrome: coreStart.chrome,
|
||||
overlays: coreStart.overlays,
|
||||
}),
|
||||
service: discover.savedSearchLoader,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
@ -122,7 +122,9 @@ export class VisualizeEmbeddableFactory
|
|||
|
||||
try {
|
||||
const savedObject = await savedVisualizations.get(savedObjectId);
|
||||
const vis = new Vis(savedObject.visState.type, await convertToSerializedVis(savedObject));
|
||||
const visState = convertToSerializedVis(savedObject);
|
||||
const vis = new Vis(savedObject.visState.type, visState);
|
||||
await vis.setState(visState);
|
||||
return createVisEmbeddableFromObject(this.deps)(vis, input, parent);
|
||||
} catch (e) {
|
||||
console.error(e); // eslint-disable-line no-console
|
||||
|
|
|
@ -398,6 +398,7 @@ describe('visualize loader pipeline helpers: build pipeline', () => {
|
|||
return aggs;
|
||||
},
|
||||
} as any,
|
||||
searchSource: {} as any,
|
||||
},
|
||||
isHierarchical: () => {
|
||||
return false;
|
||||
|
@ -473,6 +474,7 @@ describe('visualize loader pipeline helpers: build pipeline', () => {
|
|||
return aggs;
|
||||
},
|
||||
} as any,
|
||||
searchSource: {} as any,
|
||||
},
|
||||
isHierarchical: () => {
|
||||
return false;
|
||||
|
|
|
@ -43,6 +43,7 @@ import {
|
|||
setAggs,
|
||||
setChrome,
|
||||
setOverlays,
|
||||
setSavedSearchLoader,
|
||||
} from './services';
|
||||
import {
|
||||
VISUALIZE_EMBEDDABLE_TYPE,
|
||||
|
@ -70,6 +71,7 @@ import {
|
|||
convertFromSerializedVis,
|
||||
convertToSerializedVis,
|
||||
} from './saved_visualizations/_saved_vis';
|
||||
import { createSavedSearchesLoader } from '../../discover/public';
|
||||
|
||||
/**
|
||||
* Interface for this plugin's returned setup/start contracts.
|
||||
|
@ -81,7 +83,7 @@ export type VisualizationsSetup = TypesSetup;
|
|||
|
||||
export interface VisualizationsStart extends TypesStart {
|
||||
savedVisualizationsLoader: SavedVisualizationsLoader;
|
||||
createVis: (visType: string, visState?: SerializedVis) => Vis;
|
||||
createVis: (visType: string, visState: SerializedVis) => Promise<Vis>;
|
||||
convertToSerializedVis: typeof convertToSerializedVis;
|
||||
convertFromSerializedVis: typeof convertFromSerializedVis;
|
||||
showNewVisModal: typeof showNewVisModal;
|
||||
|
@ -174,7 +176,14 @@ export class VisualizationsPlugin
|
|||
visualizationTypes: types,
|
||||
});
|
||||
setSavedVisualizationsLoader(savedVisualizationsLoader);
|
||||
|
||||
const savedSearchLoader = createSavedSearchesLoader({
|
||||
savedObjectsClient: core.savedObjects.client,
|
||||
indexPatterns: data.indexPatterns,
|
||||
search: data.search,
|
||||
chrome: core.chrome,
|
||||
overlays: core.overlays,
|
||||
});
|
||||
setSavedSearchLoader(savedSearchLoader);
|
||||
return {
|
||||
...types,
|
||||
showNewVisModal,
|
||||
|
@ -183,7 +192,11 @@ export class VisualizationsPlugin
|
|||
* @param {IIndexPattern} indexPattern - index pattern to use
|
||||
* @param {VisState} visState - visualization configuration
|
||||
*/
|
||||
createVis: (visType: string, visState?: SerializedVis) => new Vis(visType, visState),
|
||||
createVis: async (visType: string, visState: SerializedVis) => {
|
||||
const vis = new Vis(visType);
|
||||
await vis.setState(visState);
|
||||
return vis;
|
||||
},
|
||||
convertToSerializedVis,
|
||||
convertFromSerializedVis,
|
||||
savedVisualizationsLoader,
|
||||
|
|
|
@ -32,32 +32,25 @@ import {
|
|||
// @ts-ignore
|
||||
import { updateOldState } from '../legacy/vis_update_state';
|
||||
import { extractReferences, injectReferences } from './saved_visualization_references';
|
||||
import { IIndexPattern, ISearchSource } from '../../../../plugins/data/public';
|
||||
import { IIndexPattern } from '../../../../plugins/data/public';
|
||||
import { ISavedVis, SerializedVis } from '../types';
|
||||
import { createSavedSearchesLoader } from '../../../../plugins/discover/public';
|
||||
import { getChrome, getOverlays, getIndexPatterns, getSavedObjects, getSearch } from '../services';
|
||||
import { createSavedSearchesLoader } from '../../../discover/public';
|
||||
|
||||
export const convertToSerializedVis = async (savedVis: ISavedVis): Promise<SerializedVis> => {
|
||||
const { visState } = savedVis;
|
||||
const searchSource =
|
||||
savedVis.searchSource && (await getSearchSource(savedVis.searchSource, savedVis.savedSearchId));
|
||||
export const convertToSerializedVis = (savedVis: ISavedVis): SerializedVis => {
|
||||
const { id, title, description, visState, uiStateJSON, searchSourceFields } = savedVis;
|
||||
|
||||
const indexPattern =
|
||||
searchSource && searchSource.getField('index') ? searchSource.getField('index')!.id : undefined;
|
||||
|
||||
const aggs = indexPattern ? visState.aggs || [] : visState.aggs;
|
||||
const aggs = searchSourceFields && searchSourceFields.index ? visState.aggs || [] : visState.aggs;
|
||||
|
||||
return {
|
||||
id: savedVis.id,
|
||||
title: savedVis.title,
|
||||
id,
|
||||
title,
|
||||
type: visState.type,
|
||||
description: savedVis.description,
|
||||
description,
|
||||
params: visState.params,
|
||||
uiState: JSON.parse(savedVis.uiStateJSON || '{}'),
|
||||
uiState: JSON.parse(uiStateJSON || '{}'),
|
||||
data: {
|
||||
indexPattern,
|
||||
aggs,
|
||||
searchSource,
|
||||
searchSource: searchSourceFields!,
|
||||
savedSearchId: savedVis.savedSearchId,
|
||||
},
|
||||
};
|
||||
|
@ -74,36 +67,14 @@ export const convertFromSerializedVis = (vis: SerializedVis): ISavedVis => {
|
|||
params: vis.params,
|
||||
},
|
||||
uiStateJSON: JSON.stringify(vis.uiState),
|
||||
searchSource: vis.data.searchSource!,
|
||||
searchSourceFields: vis.data.searchSource,
|
||||
savedSearchId: vis.data.savedSearchId,
|
||||
};
|
||||
};
|
||||
|
||||
const getSearchSource = async (inputSearchSource: ISearchSource, savedSearchId?: string) => {
|
||||
const search = getSearch();
|
||||
|
||||
const searchSource = inputSearchSource.createCopy
|
||||
? inputSearchSource.createCopy()
|
||||
: search.searchSource.create({ ...(inputSearchSource as any).fields });
|
||||
|
||||
if (savedSearchId) {
|
||||
const savedSearch = await createSavedSearchesLoader({
|
||||
search,
|
||||
savedObjectsClient: getSavedObjects().client,
|
||||
indexPatterns: getIndexPatterns(),
|
||||
chrome: getChrome(),
|
||||
overlays: getOverlays(),
|
||||
}).get(savedSearchId);
|
||||
|
||||
searchSource.setParent(savedSearch.searchSource);
|
||||
}
|
||||
|
||||
searchSource!.setField('size', 0);
|
||||
return searchSource;
|
||||
};
|
||||
|
||||
export function createSavedVisClass(services: SavedObjectKibanaServices) {
|
||||
const SavedObjectClass = createSavedObjectClass(services);
|
||||
const savedSearch = createSavedSearchesLoader(services);
|
||||
|
||||
class SavedVis extends SavedObjectClass {
|
||||
public static type: string = 'visualization';
|
||||
|
@ -117,7 +88,6 @@ export function createSavedVisClass(services: SavedObjectKibanaServices) {
|
|||
};
|
||||
// Order these fields to the top, the rest are alphabetical
|
||||
public static fieldOrder = ['title', 'description'];
|
||||
public static searchSource = true;
|
||||
|
||||
constructor(opts: Record<string, unknown> | string = {}) {
|
||||
if (typeof opts !== 'object') {
|
||||
|
@ -128,7 +98,6 @@ export function createSavedVisClass(services: SavedObjectKibanaServices) {
|
|||
super({
|
||||
type: SavedVis.type,
|
||||
mapping: SavedVis.mapping,
|
||||
searchSource: SavedVis.searchSource,
|
||||
extractReferences,
|
||||
injectReferences,
|
||||
id: (opts.id as string) || '',
|
||||
|
@ -144,11 +113,11 @@ export function createSavedVisClass(services: SavedObjectKibanaServices) {
|
|||
afterESResp: async (savedObject: SavedObject) => {
|
||||
const savedVis = (savedObject as any) as ISavedVis;
|
||||
savedVis.visState = await updateOldState(savedVis.visState);
|
||||
if (savedVis.savedSearchId && savedVis.searchSource) {
|
||||
savedObject.searchSource = await getSearchSource(
|
||||
savedVis.searchSource,
|
||||
savedVis.savedSearchId
|
||||
);
|
||||
if (savedVis.searchSourceFields?.index) {
|
||||
await services.indexPatterns.get(savedVis.searchSourceFields.index as any);
|
||||
}
|
||||
if (savedVis.savedSearchId) {
|
||||
await savedSearch.get(savedVis.savedSearchId);
|
||||
}
|
||||
return (savedVis as any) as SavedObject;
|
||||
},
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
*/
|
||||
import { SavedObjectAttributes, SavedObjectReference } from '../../../../core/public';
|
||||
import { VisSavedObject } from '../types';
|
||||
import { injectSearchSourceReferences, extractSearchSourceReferences } from '../../../data/public';
|
||||
|
||||
export function extractReferences({
|
||||
attributes,
|
||||
|
@ -29,6 +30,14 @@ export function extractReferences({
|
|||
const updatedAttributes = { ...attributes };
|
||||
const updatedReferences = [...references];
|
||||
|
||||
if (updatedAttributes.searchSourceFields) {
|
||||
const [searchSource, searchSourceReferences] = extractSearchSourceReferences(
|
||||
updatedAttributes.searchSourceFields as any
|
||||
);
|
||||
updatedAttributes.searchSourceFields = searchSource;
|
||||
searchSourceReferences.forEach(r => updatedReferences.push(r));
|
||||
}
|
||||
|
||||
// Extract saved search
|
||||
if (updatedAttributes.savedSearchId) {
|
||||
updatedReferences.push({
|
||||
|
@ -66,6 +75,12 @@ export function extractReferences({
|
|||
}
|
||||
|
||||
export function injectReferences(savedObject: VisSavedObject, references: SavedObjectReference[]) {
|
||||
if (savedObject.searchSourceFields) {
|
||||
savedObject.searchSourceFields = injectSearchSourceReferences(
|
||||
savedObject.searchSourceFields as any,
|
||||
references
|
||||
);
|
||||
}
|
||||
if (savedObject.savedSearchRefName) {
|
||||
const savedSearchReference = references.find(
|
||||
reference => reference.name === savedObject.savedSearchRefName
|
||||
|
|
|
@ -38,6 +38,7 @@ import { UsageCollectionSetup } from '../../../plugins/usage_collection/public';
|
|||
import { ExpressionsStart } from '../../../plugins/expressions/public';
|
||||
import { UiActionsStart } from '../../../plugins/ui_actions/public';
|
||||
import { SavedVisualizationsLoader } from './saved_visualizations';
|
||||
import { SavedObjectLoader } from '../../saved_objects/public';
|
||||
|
||||
export const [getUISettings, setUISettings] = createGetterSetter<IUiSettingsClient>('UISettings');
|
||||
|
||||
|
@ -84,3 +85,7 @@ export const [getAggs, setAggs] = createGetterSetter<DataPublicPluginStart['sear
|
|||
export const [getOverlays, setOverlays] = createGetterSetter<OverlayStart>('Overlays');
|
||||
|
||||
export const [getChrome, setChrome] = createGetterSetter<ChromeStart>('Chrome');
|
||||
|
||||
export const [getSavedSearchLoader, setSavedSearchLoader] = createGetterSetter<SavedObjectLoader>(
|
||||
'savedSearchLoader'
|
||||
);
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
|
||||
import { SavedObject } from '../../../plugins/saved_objects/public';
|
||||
import { ISearchSource, AggConfigOptions } from '../../../plugins/data/public';
|
||||
import { AggConfigOptions, SearchSourceFields } from '../../../plugins/data/public';
|
||||
import { SerializedVis, Vis, VisParams } from './vis';
|
||||
|
||||
export { Vis, SerializedVis, VisParams };
|
||||
|
@ -45,7 +45,7 @@ export interface ISavedVis {
|
|||
title: string;
|
||||
description?: string;
|
||||
visState: SavedVisState;
|
||||
searchSource?: ISearchSource;
|
||||
searchSourceFields?: SearchSourceFields;
|
||||
uiStateJSON?: string;
|
||||
savedSearchRefName?: string;
|
||||
savedSearchId?: string;
|
||||
|
|
|
@ -18,8 +18,6 @@
|
|||
*/
|
||||
|
||||
import { Vis } from './vis';
|
||||
// @ts-ignore
|
||||
import fixturesStubbedLogstashIndexPatternProvider from '../../../fixtures/stubbed_logstash_index_pattern';
|
||||
|
||||
jest.mock('./services', () => {
|
||||
class MockVisualizationController {
|
||||
|
@ -36,7 +34,10 @@ jest.mock('./services', () => {
|
|||
|
||||
// eslint-disable-next-line
|
||||
const { BaseVisType } = require('./vis_types/base_vis_type');
|
||||
|
||||
// eslint-disable-next-line
|
||||
const { SearchSource } = require('../../data/public/search/search_source');
|
||||
// eslint-disable-next-line
|
||||
const fixturesStubbedLogstashIndexPatternProvider = require('../../../fixtures/stubbed_logstash_index_pattern');
|
||||
const visType = new BaseVisType({
|
||||
name: 'pie',
|
||||
title: 'pie',
|
||||
|
@ -51,6 +52,13 @@ jest.mock('./services', () => {
|
|||
aggs: cfg.map((aggConfig: any) => ({ ...aggConfig, toJSON: () => aggConfig })),
|
||||
}),
|
||||
}),
|
||||
getSearch: () => ({
|
||||
searchSource: {
|
||||
create: () => {
|
||||
return new SearchSource({ index: fixturesStubbedLogstashIndexPatternProvider });
|
||||
},
|
||||
},
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
|
@ -66,19 +74,15 @@ describe('Vis Class', function() {
|
|||
{ type: 'terms' as any, schema: 'segment', params: { field: 'geo.src' } },
|
||||
],
|
||||
searchSource: {
|
||||
getField: (name: string) => {
|
||||
if (name === 'index') {
|
||||
return fixturesStubbedLogstashIndexPatternProvider();
|
||||
}
|
||||
},
|
||||
createCopy: jest.fn(),
|
||||
index: '123',
|
||||
},
|
||||
},
|
||||
params: { isDonut: true },
|
||||
};
|
||||
|
||||
beforeEach(function() {
|
||||
beforeEach(async function() {
|
||||
vis = new Vis('test', stateFixture as any);
|
||||
await vis.setState(stateFixture as any);
|
||||
});
|
||||
|
||||
const verifyVis = function(visToVerify: Vis) {
|
||||
|
|
|
@ -28,21 +28,22 @@
|
|||
*/
|
||||
|
||||
import { isFunction, defaults, cloneDeep } from 'lodash';
|
||||
import { Assign } from '@kbn/utility-types';
|
||||
import { PersistedState } from './persisted_state';
|
||||
import { getTypes, getAggs } from './services';
|
||||
import { getTypes, getAggs, getSearch, getSavedSearchLoader } from './services';
|
||||
import { VisType } from './vis_types';
|
||||
import {
|
||||
IAggConfigs,
|
||||
IndexPattern,
|
||||
ISearchSource,
|
||||
AggConfigOptions,
|
||||
SearchSourceFields,
|
||||
} from '../../../plugins/data/public';
|
||||
|
||||
export interface SerializedVisData {
|
||||
expression?: string;
|
||||
aggs: AggConfigOptions[];
|
||||
indexPattern?: string;
|
||||
searchSource?: ISearchSource;
|
||||
searchSource: SearchSourceFields;
|
||||
savedSearchId?: string;
|
||||
}
|
||||
|
||||
|
@ -68,6 +69,19 @@ export interface VisParams {
|
|||
[key: string]: any;
|
||||
}
|
||||
|
||||
const getSearchSource = async (inputSearchSource: ISearchSource, savedSearchId?: string) => {
|
||||
const searchSource = inputSearchSource.createCopy();
|
||||
if (savedSearchId) {
|
||||
const savedSearch = await getSavedSearchLoader().get(savedSearchId);
|
||||
|
||||
searchSource.setParent(savedSearch.searchSource);
|
||||
}
|
||||
searchSource.setField('size', 0);
|
||||
return searchSource;
|
||||
};
|
||||
|
||||
type PartialVisState = Assign<SerializedVis, { data: Partial<SerializedVisData> }>;
|
||||
|
||||
export class Vis {
|
||||
public readonly type: VisType;
|
||||
public readonly id?: string;
|
||||
|
@ -86,8 +100,6 @@ export class Vis {
|
|||
this.params = this.getParams(visState.params);
|
||||
this.uiState = new PersistedState(visState.uiState);
|
||||
this.id = visState.id;
|
||||
|
||||
this.setState(visState || {});
|
||||
}
|
||||
|
||||
private getType(visType: string) {
|
||||
|
@ -102,7 +114,7 @@ export class Vis {
|
|||
return defaults({}, cloneDeep(params || {}), cloneDeep(this.type.visConfig.defaults || {}));
|
||||
}
|
||||
|
||||
setState(state: SerializedVis) {
|
||||
async setState(state: PartialVisState) {
|
||||
let typeChanged = false;
|
||||
if (state.type && this.type.name !== state.type) {
|
||||
// @ts-ignore
|
||||
|
@ -120,19 +132,24 @@ export class Vis {
|
|||
}
|
||||
|
||||
if (state.data && state.data.searchSource) {
|
||||
this.data.searchSource = state.data.searchSource!;
|
||||
this.data.searchSource = await getSearch().searchSource.create(state.data.searchSource!);
|
||||
this.data.indexPattern = this.data.searchSource.getField('index');
|
||||
}
|
||||
if (state.data && state.data.savedSearchId) {
|
||||
this.data.savedSearchId = state.data.savedSearchId;
|
||||
if (this.data.searchSource) {
|
||||
this.data.searchSource = await getSearchSource(
|
||||
this.data.searchSource,
|
||||
this.data.savedSearchId
|
||||
);
|
||||
this.data.indexPattern = this.data.searchSource.getField('index');
|
||||
}
|
||||
}
|
||||
if (state.data && state.data.aggs) {
|
||||
const configStates = this.initializeDefaultsFromSchemas(
|
||||
cloneDeep(state.data.aggs),
|
||||
this.type.schemas.all || []
|
||||
);
|
||||
if (state.data && (state.data.aggs || !this.data.aggs)) {
|
||||
const aggs = state.data.aggs ? cloneDeep(state.data.aggs) : [];
|
||||
const configStates = this.initializeDefaultsFromSchemas(aggs, this.type.schemas.all || []);
|
||||
if (!this.data.indexPattern) {
|
||||
if (state.data.aggs.length) {
|
||||
if (aggs.length) {
|
||||
throw new Error('trying to initialize aggs without index pattern');
|
||||
}
|
||||
return;
|
||||
|
@ -142,22 +159,31 @@ export class Vis {
|
|||
}
|
||||
|
||||
clone() {
|
||||
return new Vis(this.type.name, this.serialize());
|
||||
const { data, ...restOfSerialized } = this.serialize();
|
||||
const vis = new Vis(this.type.name, restOfSerialized as any);
|
||||
vis.setState({ ...restOfSerialized, data: {} });
|
||||
const aggs = this.data.indexPattern
|
||||
? getAggs().createAggConfigs(this.data.indexPattern, data.aggs)
|
||||
: undefined;
|
||||
vis.data = {
|
||||
...this.data,
|
||||
aggs,
|
||||
};
|
||||
return vis;
|
||||
}
|
||||
|
||||
serialize(): SerializedVis {
|
||||
const aggs = this.data.aggs ? this.data.aggs.aggs.map(agg => agg.toJSON()) : [];
|
||||
const indexPattern = this.data.searchSource && this.data.searchSource.getField('index');
|
||||
return {
|
||||
id: this.id,
|
||||
title: this.title,
|
||||
description: this.description,
|
||||
type: this.type.name,
|
||||
params: cloneDeep(this.params) as any,
|
||||
uiState: this.uiState.toJSON(),
|
||||
data: {
|
||||
aggs: aggs as any,
|
||||
indexPattern: indexPattern ? indexPattern.id : undefined,
|
||||
searchSource: this.data.searchSource!.createCopy(),
|
||||
searchSource: this.data.searchSource ? this.data.searchSource.getSerializedFields() : {},
|
||||
savedSearchId: this.data.savedSearchId,
|
||||
},
|
||||
};
|
||||
|
|
|
@ -645,8 +645,7 @@ function VisualizeAppController($scope, $route, $injector, $timeout, kbnUrlState
|
|||
title: savedVis.title,
|
||||
type: savedVis.type || stateContainer.getState().vis.type,
|
||||
});
|
||||
savedVis.searchSource.setField('query', stateContainer.getState().query);
|
||||
savedVis.searchSource.setField('filter', stateContainer.getState().filters);
|
||||
savedVis.searchSourceFields = searchSource.getSerializedFields();
|
||||
savedVis.visState = stateContainer.getState().vis;
|
||||
savedVis.uiStateJSON = angular.toJson($scope.uiState.toJSON());
|
||||
$appStatus.dirty = false;
|
||||
|
|
|
@ -45,9 +45,9 @@ const getResolvedResults = deps => {
|
|||
|
||||
return savedVis => {
|
||||
results.savedVis = savedVis;
|
||||
const serializedVis = visualizations.convertToSerializedVis(savedVis);
|
||||
return visualizations
|
||||
.convertToSerializedVis(savedVis)
|
||||
.then(serializedVis => visualizations.createVis(serializedVis.type, serializedVis))
|
||||
.createVis(serializedVis.type, serializedVis)
|
||||
.then(vis => {
|
||||
if (vis.type.setup) {
|
||||
return vis.type.setup(vis).catch(() => vis);
|
||||
|
@ -171,6 +171,10 @@ export function initVisualizeApp(app, deps) {
|
|||
return data.indexPatterns
|
||||
.ensureDefaultIndexPattern(history)
|
||||
.then(() => savedVisualizations.get($route.current.params))
|
||||
.then(savedVis => {
|
||||
savedVis.searchSourceFields = { index: $route.current.params.indexPattern };
|
||||
return savedVis;
|
||||
})
|
||||
.then(getResolvedResults(deps))
|
||||
.then(delay)
|
||||
.catch(
|
||||
|
|
|
@ -399,7 +399,7 @@ export class ESSearchSource extends AbstractESSource {
|
|||
}
|
||||
|
||||
const searchService = getSearchService();
|
||||
const searchSource = searchService.searchSource.create();
|
||||
const searchSource = searchService.searchSource.createEmpty();
|
||||
|
||||
searchSource.setField('index', indexPattern);
|
||||
searchSource.setField('size', 1);
|
||||
|
|
|
@ -125,7 +125,7 @@ export class AbstractESSource extends AbstractVectorSource {
|
|||
allFilters.push(getTimeFilter().createFilter(indexPattern, searchFilters.timeFilters));
|
||||
}
|
||||
const searchService = getSearchService();
|
||||
const searchSource = searchService.searchSource.create(initialSearchContext);
|
||||
const searchSource = await searchService.searchSource.create(initialSearchContext);
|
||||
|
||||
searchSource.setField('index', indexPattern);
|
||||
searchSource.setField('size', limit);
|
||||
|
@ -135,7 +135,7 @@ export class AbstractESSource extends AbstractVectorSource {
|
|||
}
|
||||
|
||||
if (searchFilters.sourceQuery) {
|
||||
const layerSearchSource = searchService.searchSource.create();
|
||||
const layerSearchSource = searchService.searchSource.createEmpty();
|
||||
|
||||
layerSearchSource.setField('index', indexPattern);
|
||||
layerSearchSource.setField('query', searchFilters.sourceQuery);
|
||||
|
@ -296,7 +296,7 @@ export class AbstractESSource extends AbstractVectorSource {
|
|||
|
||||
const indexPattern = await this.getIndexPattern();
|
||||
const searchService = getSearchService();
|
||||
const searchSource = searchService.searchSource.create();
|
||||
const searchSource = searchService.searchSource.createEmpty();
|
||||
|
||||
searchSource.setField('index', indexPattern);
|
||||
searchSource.setField('size', 0);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue