enabling esdsl function in expressions on server side (#95304)
This commit is contained in:
parent
44a46358c2
commit
a62d69d7ec
194
src/plugins/data/common/search/expressions/esdsl.ts
Normal file
194
src/plugins/data/common/search/expressions/esdsl.ts
Normal file
|
@ -0,0 +1,194 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common';
|
||||
|
||||
import { EsRawResponse } from './es_raw_response';
|
||||
import { RequestStatistics, RequestAdapter } from '../../../../inspector/common';
|
||||
import { ISearchGeneric, KibanaContext } from '..';
|
||||
import { buildEsQuery, getEsQueryConfig } from '../../es_query/es_query';
|
||||
import { UiSettingsCommon } from '../../index_patterns';
|
||||
|
||||
const name = 'esdsl';
|
||||
|
||||
type Input = KibanaContext | null;
|
||||
type Output = Promise<EsRawResponse>;
|
||||
|
||||
interface Arguments {
|
||||
dsl: string;
|
||||
index: string;
|
||||
size: number;
|
||||
}
|
||||
|
||||
export type EsdslExpressionFunctionDefinition = ExpressionFunctionDefinition<
|
||||
typeof name,
|
||||
Input,
|
||||
Arguments,
|
||||
Output
|
||||
>;
|
||||
|
||||
/** @internal */
|
||||
export interface EsdslStartDependencies {
|
||||
search: ISearchGeneric;
|
||||
uiSettingsClient: UiSettingsCommon;
|
||||
}
|
||||
|
||||
export const getEsdslFn = ({
|
||||
getStartDependencies,
|
||||
}: {
|
||||
getStartDependencies: (getKibanaRequest: any) => Promise<EsdslStartDependencies>;
|
||||
}) => {
|
||||
const esdsl: EsdslExpressionFunctionDefinition = {
|
||||
name,
|
||||
type: 'es_raw_response',
|
||||
inputTypes: ['kibana_context', 'null'],
|
||||
help: i18n.translate('data.search.esdsl.help', {
|
||||
defaultMessage: 'Run Elasticsearch request',
|
||||
}),
|
||||
args: {
|
||||
dsl: {
|
||||
types: ['string'],
|
||||
aliases: ['_', 'q', 'query'],
|
||||
help: i18n.translate('data.search.esdsl.q.help', {
|
||||
defaultMessage: 'Query DSL',
|
||||
}),
|
||||
required: true,
|
||||
},
|
||||
index: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.esdsl.index.help', {
|
||||
defaultMessage: 'ElasticSearch index to query',
|
||||
}),
|
||||
required: true,
|
||||
},
|
||||
size: {
|
||||
types: ['number'],
|
||||
help: i18n.translate('data.search.esdsl.size.help', {
|
||||
defaultMessage: 'ElasticSearch searchAPI size parameter',
|
||||
}),
|
||||
default: 10,
|
||||
},
|
||||
},
|
||||
async fn(input, args, { inspectorAdapters, abortSignal, getKibanaRequest }) {
|
||||
const { search, uiSettingsClient } = await getStartDependencies(getKibanaRequest);
|
||||
|
||||
const dsl = JSON.parse(args.dsl);
|
||||
|
||||
if (input) {
|
||||
const esQueryConfigs = getEsQueryConfig(uiSettingsClient as any);
|
||||
const query = buildEsQuery(
|
||||
undefined, // args.index,
|
||||
input.query || [],
|
||||
input.filters || [],
|
||||
esQueryConfigs
|
||||
);
|
||||
|
||||
if (dsl.query) {
|
||||
query.bool.must.push(dsl.query);
|
||||
}
|
||||
|
||||
dsl.query = query;
|
||||
}
|
||||
|
||||
if (!inspectorAdapters.requests) {
|
||||
inspectorAdapters.requests = new RequestAdapter();
|
||||
}
|
||||
|
||||
const request = inspectorAdapters.requests.start(
|
||||
i18n.translate('data.search.dataRequest.title', {
|
||||
defaultMessage: 'Data',
|
||||
}),
|
||||
{
|
||||
description: i18n.translate('data.search.es_search.dataRequest.description', {
|
||||
defaultMessage:
|
||||
'This request queries Elasticsearch to fetch the data for the visualization.',
|
||||
}),
|
||||
}
|
||||
);
|
||||
|
||||
request.stats({
|
||||
indexPattern: {
|
||||
label: i18n.translate('data.search.es_search.indexPatternLabel', {
|
||||
defaultMessage: 'Index pattern',
|
||||
}),
|
||||
value: args.index,
|
||||
description: i18n.translate('data.search.es_search.indexPatternDescription', {
|
||||
defaultMessage: 'The index pattern that connected to the Elasticsearch indices.',
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
try {
|
||||
const { rawResponse } = await search(
|
||||
{
|
||||
params: {
|
||||
index: args.index,
|
||||
size: args.size,
|
||||
body: dsl,
|
||||
},
|
||||
},
|
||||
{ abortSignal }
|
||||
).toPromise();
|
||||
|
||||
const stats: RequestStatistics = {};
|
||||
|
||||
if (rawResponse?.took) {
|
||||
stats.queryTime = {
|
||||
label: i18n.translate('data.search.es_search.queryTimeLabel', {
|
||||
defaultMessage: 'Query time',
|
||||
}),
|
||||
value: i18n.translate('data.search.es_search.queryTimeValue', {
|
||||
defaultMessage: '{queryTime}ms',
|
||||
values: { queryTime: rawResponse.took },
|
||||
}),
|
||||
description: i18n.translate('data.search.es_search.queryTimeDescription', {
|
||||
defaultMessage:
|
||||
'The time it took to process the query. ' +
|
||||
'Does not include the time to send the request or parse it in the browser.',
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
if (rawResponse?.hits) {
|
||||
stats.hitsTotal = {
|
||||
label: i18n.translate('data.search.es_search.hitsTotalLabel', {
|
||||
defaultMessage: 'Hits (total)',
|
||||
}),
|
||||
value: `${rawResponse.hits.total}`,
|
||||
description: i18n.translate('data.search.es_search.hitsTotalDescription', {
|
||||
defaultMessage: 'The number of documents that match the query.',
|
||||
}),
|
||||
};
|
||||
|
||||
stats.hits = {
|
||||
label: i18n.translate('data.search.es_search.hitsLabel', {
|
||||
defaultMessage: 'Hits',
|
||||
}),
|
||||
value: `${rawResponse.hits.hits.length}`,
|
||||
description: i18n.translate('data.search.es_search.hitsDescription', {
|
||||
defaultMessage: 'The number of documents returned by the query.',
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
request.stats(stats).ok({ json: rawResponse });
|
||||
request.json(dsl);
|
||||
|
||||
return {
|
||||
type: 'es_raw_response',
|
||||
body: rawResponse,
|
||||
};
|
||||
} catch (e) {
|
||||
request.error({ json: e });
|
||||
throw e;
|
||||
}
|
||||
},
|
||||
};
|
||||
return esdsl;
|
||||
};
|
|
@ -23,3 +23,5 @@ export * from './range_filter';
|
|||
export * from './kibana_filter';
|
||||
export * from './filters_to_ast';
|
||||
export * from './timerange';
|
||||
export * from './es_raw_response';
|
||||
export * from './esdsl';
|
||||
|
|
|
@ -40,7 +40,6 @@ import { EventEmitter } from 'events';
|
|||
import { ExecutionContext } from 'src/plugins/expressions/common';
|
||||
import { ExpressionAstExpression } from 'src/plugins/expressions/common';
|
||||
import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common';
|
||||
import { ExpressionFunctionDefinition as ExpressionFunctionDefinition_2 } from 'src/plugins/expressions/public';
|
||||
import { ExpressionsSetup } from 'src/plugins/expressions/public';
|
||||
import { ExpressionValueBoxed } from 'src/plugins/expressions/common';
|
||||
import { FormatFactory as FormatFactory_2 } from 'src/plugins/data/common/field_formats/utils';
|
||||
|
@ -727,7 +726,7 @@ export type EsaggsExpressionFunctionDefinition = ExpressionFunctionDefinition<'e
|
|||
// Warning: (ae-missing-release-tag) "EsdslExpressionFunctionDefinition" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export type EsdslExpressionFunctionDefinition = ExpressionFunctionDefinition_2<typeof name_3, Input_36, Arguments_23, Output_36>;
|
||||
export type EsdslExpressionFunctionDefinition = ExpressionFunctionDefinition<typeof name_3, Input_36, Arguments_23, Output_36>;
|
||||
|
||||
// Warning: (ae-missing-release-tag) "esFilters" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
|
|
|
@ -6,7 +6,12 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { esdsl } from './esdsl';
|
||||
import { getEsdsl } from './esdsl';
|
||||
import { MockedKeys } from '@kbn/utility-types/target/jest';
|
||||
import { EsdslExpressionFunctionDefinition } from '../../../common/search/expressions';
|
||||
import { StartServicesAccessor } from 'kibana/public';
|
||||
import { DataPublicPluginStart, DataStartDependencies } from '../../types';
|
||||
import { of } from 'rxjs';
|
||||
|
||||
jest.mock('@kbn/i18n', () => {
|
||||
return {
|
||||
|
@ -16,26 +21,38 @@ jest.mock('@kbn/i18n', () => {
|
|||
};
|
||||
});
|
||||
|
||||
jest.mock('../../services', () => ({
|
||||
getUiSettings: () => ({
|
||||
get: () => true,
|
||||
}),
|
||||
getSearchService: () => ({
|
||||
search: jest.fn((params: any) => {
|
||||
return {
|
||||
toPromise: async () => {
|
||||
return { rawResponse: params };
|
||||
},
|
||||
};
|
||||
}),
|
||||
}),
|
||||
}));
|
||||
|
||||
describe('esdsl', () => {
|
||||
let getStartServices: StartServicesAccessor<DataStartDependencies, DataPublicPluginStart>;
|
||||
let startDependencies: MockedKeys<
|
||||
StartServicesAccessor<DataStartDependencies, DataPublicPluginStart>
|
||||
>;
|
||||
let esdsl: EsdslExpressionFunctionDefinition;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
startDependencies = [
|
||||
{
|
||||
uiSettings: {
|
||||
get: jest.fn().mockReturnValue(true),
|
||||
},
|
||||
},
|
||||
{},
|
||||
{
|
||||
search: {
|
||||
search: jest.fn((params: any) => of({ rawResponse: params })),
|
||||
},
|
||||
},
|
||||
];
|
||||
getStartServices = jest
|
||||
.fn()
|
||||
.mockResolvedValue(new Promise((resolve) => resolve(startDependencies)));
|
||||
esdsl = getEsdsl({ getStartServices });
|
||||
});
|
||||
|
||||
describe('correctly handles input', () => {
|
||||
test('throws on invalid json input', async () => {
|
||||
const fn = async function () {
|
||||
await esdsl().fn(null, { dsl: 'invalid json', index: 'test', size: 0 }, {
|
||||
await esdsl.fn(null, { dsl: 'invalid json', index: 'test', size: 0 }, {
|
||||
inspectorAdapters: {},
|
||||
} as any);
|
||||
};
|
||||
|
@ -50,7 +67,7 @@ describe('esdsl', () => {
|
|||
});
|
||||
|
||||
test('adds filters', async () => {
|
||||
const result = await esdsl().fn(
|
||||
const result = await esdsl.fn(
|
||||
{
|
||||
type: 'kibana_context',
|
||||
filters: [
|
||||
|
@ -68,7 +85,7 @@ describe('esdsl', () => {
|
|||
});
|
||||
|
||||
test('adds filters to query with filters', async () => {
|
||||
const result = await esdsl().fn(
|
||||
const result = await esdsl.fn(
|
||||
{
|
||||
type: 'kibana_context',
|
||||
filters: [
|
||||
|
@ -90,7 +107,7 @@ describe('esdsl', () => {
|
|||
});
|
||||
|
||||
test('adds query', async () => {
|
||||
const result = await esdsl().fn(
|
||||
const result = await esdsl.fn(
|
||||
{
|
||||
type: 'kibana_context',
|
||||
query: { language: 'lucene', query: '*' },
|
||||
|
@ -103,7 +120,7 @@ describe('esdsl', () => {
|
|||
});
|
||||
|
||||
test('adds query to a query with filters', async () => {
|
||||
const result = await esdsl().fn(
|
||||
const result = await esdsl.fn(
|
||||
{
|
||||
type: 'kibana_context',
|
||||
query: { language: 'lucene', query: '*' },
|
||||
|
@ -120,7 +137,7 @@ describe('esdsl', () => {
|
|||
});
|
||||
|
||||
test('ignores timerange', async () => {
|
||||
const result = await esdsl().fn(
|
||||
const result = await esdsl.fn(
|
||||
{
|
||||
type: 'kibana_context',
|
||||
timeRange: { from: 'now-15m', to: 'now' },
|
||||
|
@ -134,7 +151,7 @@ describe('esdsl', () => {
|
|||
});
|
||||
|
||||
test('correctly handles filter, query and timerange on context', async () => {
|
||||
const result = await esdsl().fn(
|
||||
const result = await esdsl.fn(
|
||||
{
|
||||
type: 'kibana_context',
|
||||
query: { language: 'lucene', query: '*' },
|
||||
|
|
|
@ -6,182 +6,37 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { ExpressionFunctionDefinition } from 'src/plugins/expressions/public';
|
||||
|
||||
import { getSearchService, getUiSettings } from '../../services';
|
||||
import { EsRawResponse } from './es_raw_response';
|
||||
import { RequestStatistics, RequestAdapter } from '../../../../inspector/common';
|
||||
import { IEsSearchResponse, KibanaContext } from '../../../common/search';
|
||||
import { buildEsQuery, getEsQueryConfig } from '../../../common/es_query/es_query';
|
||||
import { DataPublicPluginStart } from '../../types';
|
||||
|
||||
const name = 'esdsl';
|
||||
|
||||
type Input = KibanaContext | null;
|
||||
type Output = Promise<EsRawResponse>;
|
||||
|
||||
interface Arguments {
|
||||
dsl: string;
|
||||
index: string;
|
||||
size: number;
|
||||
}
|
||||
|
||||
export type EsdslExpressionFunctionDefinition = ExpressionFunctionDefinition<
|
||||
typeof name,
|
||||
Input,
|
||||
Arguments,
|
||||
Output
|
||||
>;
|
||||
|
||||
export const esdsl = (): EsdslExpressionFunctionDefinition => ({
|
||||
name,
|
||||
type: 'es_raw_response',
|
||||
inputTypes: ['kibana_context', 'null'],
|
||||
help: i18n.translate('data.search.esdsl.help', {
|
||||
defaultMessage: 'Run Elasticsearch request',
|
||||
}),
|
||||
args: {
|
||||
dsl: {
|
||||
types: ['string'],
|
||||
aliases: ['_', 'q', 'query'],
|
||||
help: i18n.translate('data.search.esdsl.q.help', {
|
||||
defaultMessage: 'Query DSL',
|
||||
}),
|
||||
required: true,
|
||||
},
|
||||
index: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('data.search.esdsl.index.help', {
|
||||
defaultMessage: 'ElasticSearch index to query',
|
||||
}),
|
||||
required: true,
|
||||
},
|
||||
size: {
|
||||
types: ['number'],
|
||||
help: i18n.translate('data.search.esdsl.size.help', {
|
||||
defaultMessage: 'ElasticSearch searchAPI size parameter',
|
||||
}),
|
||||
default: 10,
|
||||
},
|
||||
},
|
||||
async fn(input, args, { inspectorAdapters, abortSignal }) {
|
||||
const searchService: DataPublicPluginStart['search'] = getSearchService();
|
||||
|
||||
const dsl = JSON.parse(args.dsl);
|
||||
|
||||
if (input) {
|
||||
const esQueryConfigs = getEsQueryConfig(getUiSettings());
|
||||
const query = buildEsQuery(
|
||||
undefined, // args.index,
|
||||
input.query || [],
|
||||
input.filters || [],
|
||||
esQueryConfigs
|
||||
);
|
||||
|
||||
if (!dsl.query) {
|
||||
dsl.query = query;
|
||||
} else {
|
||||
query.bool.must.push(dsl.query);
|
||||
dsl.query = query;
|
||||
}
|
||||
}
|
||||
|
||||
if (!inspectorAdapters.requests) {
|
||||
inspectorAdapters.requests = new RequestAdapter();
|
||||
}
|
||||
|
||||
const request = inspectorAdapters.requests.start(
|
||||
i18n.translate('data.search.dataRequest.title', {
|
||||
defaultMessage: 'Data',
|
||||
}),
|
||||
{
|
||||
description: i18n.translate('data.search.es_search.dataRequest.description', {
|
||||
defaultMessage:
|
||||
'This request queries Elasticsearch to fetch the data for the visualization.',
|
||||
}),
|
||||
}
|
||||
);
|
||||
|
||||
request.stats({
|
||||
indexPattern: {
|
||||
label: i18n.translate('data.search.es_search.indexPatternLabel', {
|
||||
defaultMessage: 'Index pattern',
|
||||
}),
|
||||
value: args.index,
|
||||
description: i18n.translate('data.search.es_search.indexPatternDescription', {
|
||||
defaultMessage: 'The index pattern that connected to the Elasticsearch indices.',
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
let res: IEsSearchResponse;
|
||||
try {
|
||||
res = await searchService
|
||||
.search(
|
||||
{
|
||||
params: {
|
||||
index: args.index,
|
||||
size: args.size,
|
||||
body: dsl,
|
||||
},
|
||||
},
|
||||
{ abortSignal }
|
||||
)
|
||||
.toPromise();
|
||||
|
||||
const stats: RequestStatistics = {};
|
||||
const resp = res.rawResponse;
|
||||
|
||||
if (resp && resp.took) {
|
||||
stats.queryTime = {
|
||||
label: i18n.translate('data.search.es_search.queryTimeLabel', {
|
||||
defaultMessage: 'Query time',
|
||||
}),
|
||||
value: i18n.translate('data.search.es_search.queryTimeValue', {
|
||||
defaultMessage: '{queryTime}ms',
|
||||
values: { queryTime: resp.took },
|
||||
}),
|
||||
description: i18n.translate('data.search.es_search.queryTimeDescription', {
|
||||
defaultMessage:
|
||||
'The time it took to process the query. ' +
|
||||
'Does not include the time to send the request or parse it in the browser.',
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
if (resp && resp.hits) {
|
||||
stats.hitsTotal = {
|
||||
label: i18n.translate('data.search.es_search.hitsTotalLabel', {
|
||||
defaultMessage: 'Hits (total)',
|
||||
}),
|
||||
value: `${resp.hits.total}`,
|
||||
description: i18n.translate('data.search.es_search.hitsTotalDescription', {
|
||||
defaultMessage: 'The number of documents that match the query.',
|
||||
}),
|
||||
};
|
||||
|
||||
stats.hits = {
|
||||
label: i18n.translate('data.search.es_search.hitsLabel', {
|
||||
defaultMessage: 'Hits',
|
||||
}),
|
||||
value: `${resp.hits.hits.length}`,
|
||||
description: i18n.translate('data.search.es_search.hitsDescription', {
|
||||
defaultMessage: 'The number of documents returned by the query.',
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
request.stats(stats).ok({ json: resp });
|
||||
request.json(dsl);
|
||||
import { StartServicesAccessor } from 'src/core/public';
|
||||
import { DataPublicPluginStart, DataStartDependencies } from '../../types';
|
||||
import { getEsdslFn } from '../../../common/search/expressions/esdsl';
|
||||
import { UiSettingsCommon } from '../../../common/index_patterns';
|
||||
|
||||
/**
|
||||
* This is some glue code that takes in `core.getStartServices`, extracts the dependencies
|
||||
* needed for this function, and wraps them behind a `getStartDependencies` function that
|
||||
* is then called at runtime.
|
||||
*
|
||||
* We do this so that we can be explicit about exactly which dependencies the function
|
||||
* requires, without cluttering up the top-level `plugin.ts` with this logic. It also
|
||||
* makes testing the expression function a bit easier since `getStartDependencies` is
|
||||
* the only thing you should need to mock.
|
||||
*
|
||||
* @param getStartServices - core's StartServicesAccessor for this plugin
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export function getEsdsl({
|
||||
getStartServices,
|
||||
}: {
|
||||
getStartServices: StartServicesAccessor<DataStartDependencies, DataPublicPluginStart>;
|
||||
}) {
|
||||
return getEsdslFn({
|
||||
getStartDependencies: async () => {
|
||||
const [core, , { search }] = await getStartServices();
|
||||
return {
|
||||
type: 'es_raw_response',
|
||||
body: resp,
|
||||
uiSettingsClient: (core.uiSettings as any) as UiSettingsCommon,
|
||||
search: search.search,
|
||||
};
|
||||
} catch (e) {
|
||||
request.error({ json: e });
|
||||
throw e;
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
|
@ -6,6 +6,6 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export * from './es_raw_response';
|
||||
export * from './esaggs';
|
||||
export * from './esdsl';
|
||||
export * from '../../../common/search/expressions';
|
||||
|
|
|
@ -33,6 +33,7 @@ import {
|
|||
rangeFilterFunction,
|
||||
kibanaFilterFunction,
|
||||
phraseFilterFunction,
|
||||
esRawResponse,
|
||||
} from '../../common/search';
|
||||
import { getCallMsearch } from './legacy';
|
||||
import { AggsService, AggsStartDependencies } from './aggs';
|
||||
|
@ -40,7 +41,7 @@ import { IndexPatternsContract } from '../index_patterns/index_patterns';
|
|||
import { ISearchInterceptor, SearchInterceptor } from './search_interceptor';
|
||||
import { SearchUsageCollector, createUsageCollector } from './collectors';
|
||||
import { UsageCollectionSetup } from '../../../usage_collection/public';
|
||||
import { esdsl, esRawResponse, getEsaggs } from './expressions';
|
||||
import { getEsaggs, getEsdsl } from './expressions';
|
||||
import { ExpressionsSetup } from '../../../expressions/public';
|
||||
import { ISessionsClient, ISessionService, SessionsClient, SessionService } from './session';
|
||||
import { ConfigSchema } from '../../config';
|
||||
|
@ -126,7 +127,11 @@ export class SearchService implements Plugin<ISearchSetup, ISearchStart> {
|
|||
expressions.registerFunction(phraseFilterFunction);
|
||||
expressions.registerType(kibanaContext);
|
||||
|
||||
expressions.registerFunction(esdsl);
|
||||
expressions.registerFunction(
|
||||
getEsdsl({ getStartServices } as {
|
||||
getStartServices: StartServicesAccessor<DataStartDependencies, DataPublicPluginStart>;
|
||||
})
|
||||
);
|
||||
expressions.registerType(esRawResponse);
|
||||
|
||||
const aggs = this.aggsService.setup({
|
||||
|
|
46
src/plugins/data/server/search/expressions/esdsl.ts
Normal file
46
src/plugins/data/server/search/expressions/esdsl.ts
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { StartServicesAccessor } from 'src/core/server';
|
||||
import { DataPluginStart, DataPluginStartDependencies } from '../../plugin';
|
||||
import { getEsdslFn } from '../../../common/search/expressions/esdsl';
|
||||
|
||||
/**
|
||||
* This is some glue code that takes in `core.getStartServices`, extracts the dependencies
|
||||
* needed for this function, and wraps them behind a `getStartDependencies` function that
|
||||
* is then called at runtime.
|
||||
*
|
||||
* We do this so that we can be explicit about exactly which dependencies the function
|
||||
* requires, without cluttering up the top-level `plugin.ts` with this logic. It also
|
||||
* makes testing the expression function a bit easier since `getStartDependencies` is
|
||||
* the only thing you should need to mock.
|
||||
*
|
||||
* @param getStartServices - core's StartServicesAccessor for this plugin
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export function getEsdsl({
|
||||
getStartServices,
|
||||
}: {
|
||||
getStartServices: StartServicesAccessor<DataPluginStartDependencies, DataPluginStart>;
|
||||
}) {
|
||||
return getEsdslFn({
|
||||
getStartDependencies: async (getKibanaRequest: any) => {
|
||||
const [core, , { search }] = await getStartServices();
|
||||
if (!getKibanaRequest || !getKibanaRequest()) {
|
||||
throw new Error('TODO: add text');
|
||||
}
|
||||
const request = getKibanaRequest();
|
||||
const savedObjectsClient = core.savedObjects.getScopedClient(request);
|
||||
return {
|
||||
uiSettingsClient: core.uiSettings.asScopedToClient(savedObjectsClient),
|
||||
search: search.asScoped(request).search,
|
||||
};
|
||||
},
|
||||
});
|
||||
}
|
|
@ -7,3 +7,4 @@
|
|||
*/
|
||||
|
||||
export * from './esaggs';
|
||||
export * from './esdsl';
|
||||
|
|
|
@ -63,8 +63,9 @@ import {
|
|||
searchSourceRequiredUiSettings,
|
||||
SearchSourceService,
|
||||
phraseFilterFunction,
|
||||
esRawResponse,
|
||||
} from '../../common/search';
|
||||
import { getEsaggs } from './expressions';
|
||||
import { getEsaggs, getEsdsl } from './expressions';
|
||||
import {
|
||||
getShardDelayBucketAgg,
|
||||
SHARD_DELAY_AGG_NAME,
|
||||
|
@ -150,6 +151,7 @@ export class SearchService implements Plugin<ISearchSetup, ISearchStart> {
|
|||
}
|
||||
|
||||
expressions.registerFunction(getEsaggs({ getStartServices: core.getStartServices }));
|
||||
expressions.registerFunction(getEsdsl({ getStartServices: core.getStartServices }));
|
||||
expressions.registerFunction(kibana);
|
||||
expressions.registerFunction(luceneFunction);
|
||||
expressions.registerFunction(kqlFunction);
|
||||
|
@ -162,6 +164,7 @@ export class SearchService implements Plugin<ISearchSetup, ISearchStart> {
|
|||
expressions.registerFunction(rangeFilterFunction);
|
||||
expressions.registerFunction(phraseFilterFunction);
|
||||
expressions.registerType(kibanaContext);
|
||||
expressions.registerType(esRawResponse);
|
||||
|
||||
const aggs = this.aggsService.setup({ registerFunction: expressions.registerFunction });
|
||||
|
||||
|
|
Loading…
Reference in a new issue