[Canvas] Improvements to datasource expressions including SQL parameter support and array leniency (#99549)

* Remove es sql strategy from behind Labs project, remove legacy essql code, remove last spot of legacy elasticsearch client from canvas

* clean up test

* fix es field test

* remove comment

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Poff Poffenberger 2021-06-28 13:19:22 -05:00 committed by GitHub
parent b51af01adc
commit 9c2605398f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 219 additions and 556 deletions

View file

@ -9,11 +9,10 @@
import { i18n } from '@kbn/i18n';
export const LABS_PROJECT_PREFIX = 'labs:';
export const USE_DATA_SERVICE = `${LABS_PROJECT_PREFIX}canvas:useDataService` as const;
export const TIME_TO_PRESENT = `${LABS_PROJECT_PREFIX}presentation:timeToPresent` as const;
export const DEFER_BELOW_FOLD = `${LABS_PROJECT_PREFIX}dashboard:deferBelowFold` as const;
export const projectIDs = [TIME_TO_PRESENT, USE_DATA_SERVICE, DEFER_BELOW_FOLD] as const;
export const projectIDs = [TIME_TO_PRESENT, DEFER_BELOW_FOLD] as const;
export const environmentNames = ['kibana', 'browser', 'session'] as const;
export const solutionNames = ['canvas', 'dashboard', 'presentation'] as const;
@ -35,22 +34,6 @@ export const projects: { [ID in ProjectID]: ProjectConfig & { id: ID } } = {
}),
solutions: ['canvas'],
},
[USE_DATA_SERVICE]: {
id: USE_DATA_SERVICE,
isActive: true,
isDisplayed: true,
environments: ['kibana', 'browser', 'session'],
name: i18n.translate('presentationUtil.experiments.enableUseDataServiceExperimentName', {
defaultMessage: 'Use data service',
}),
description: i18n.translate(
'presentationUtil.experiments.enableUseDataServiceExperimentDescription',
{
defaultMessage: 'An experiment of using the new data.search service for Canvas datasources',
}
),
solutions: ['canvas'],
},
[DEFER_BELOW_FOLD]: {
id: DEFER_BELOW_FOLD,
isActive: false,

View file

@ -1,79 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import {
ExpressionFunctionDefinition,
ExpressionValueFilter,
} from 'src/plugins/expressions/common';
// @ts-expect-error untyped local
import { buildESRequest } from '../../../common/lib/request/build_es_request';
import { getFunctionHelp } from '../../../i18n';
interface Arguments {
index: string | null;
query: string;
}
export function escount(): ExpressionFunctionDefinition<
'escount',
ExpressionValueFilter,
Arguments,
any
> {
const { help, args: argHelp } = getFunctionHelp().escount;
return {
name: 'escount',
type: 'number',
context: {
types: ['filter'],
},
help,
args: {
query: {
types: ['string'],
aliases: ['_', 'q'],
help: argHelp.query,
default: '"-_index:.kibana"',
},
index: {
types: ['string'],
default: '_all',
help: argHelp.index,
},
},
fn: (input, args, handlers) => {
input.and = input.and.concat([
{
type: 'filter',
filterType: 'luceneQueryString',
query: args.query,
and: [],
},
]);
const esRequest = buildESRequest(
{
index: args.index,
body: {
query: {
bool: {
must: [{ match_all: {} }],
},
},
},
},
input
);
return ((handlers as any) as { elasticsearchClient: any })
.elasticsearchClient('count', esRequest)
.then((resp: { count: number }) => resp.count);
},
};
}

View file

@ -1,114 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import squel from 'safe-squel';
import { ExpressionFunctionDefinition } from 'src/plugins/expressions';
/* eslint-disable */
import { queryEsSQL } from '../../../server/lib/query_es_sql';
/* eslint-enable */
import { ExpressionValueFilter } from '../../../types';
import { getFunctionHelp } from '../../../i18n';
interface Arguments {
index: string;
query: string;
sort: string;
fields: string;
metaFields: string;
count: number;
}
export function esdocs(): ExpressionFunctionDefinition<
'esdocs',
ExpressionValueFilter,
Arguments,
any
> {
const { help, args: argHelp } = getFunctionHelp().esdocs;
return {
name: 'esdocs',
type: 'datatable',
context: {
types: ['filter'],
},
help,
args: {
query: {
types: ['string'],
aliases: ['_', 'q'],
help: argHelp.query,
default: '-_index:.kibana',
},
count: {
types: ['number'],
default: 1000,
help: argHelp.count,
},
fields: {
help: argHelp.fields,
types: ['string'],
},
index: {
types: ['string'],
default: '_all',
help: argHelp.index,
},
// TODO: This arg isn't being used in the function.
// We need to restore this functionality or remove it as an arg.
metaFields: {
help: argHelp.metaFields,
types: ['string'],
},
sort: {
types: ['string'],
help: argHelp.sort,
},
},
fn: (input, args, context) => {
const { count, index, fields, sort } = args;
input.and = input.and.concat([
{
type: 'filter',
filterType: 'luceneQueryString',
query: args.query,
and: [],
},
]);
let query = squel.select({
autoQuoteTableNames: true,
autoQuoteFieldNames: true,
autoQuoteAliasNames: true,
nameQuoteCharacter: '"',
});
if (index) {
query.from(index);
}
if (fields) {
const allFields = fields.split(',').map((field) => field.trim());
allFields.forEach((field) => (query = query.field(field)));
}
if (sort) {
const [sortField, sortOrder] = sort.split(',').map((str) => str.trim());
if (sortField) {
query.order(`"${sortField}"`, sortOrder === 'asc');
}
}
return queryEsSQL(((context as any) as { elasticsearchClient: any }).elasticsearchClient, {
count,
query: query.toString(),
filter: input.and,
});
},
};
}

View file

@ -1,61 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common';
/* eslint-disable */
import { queryEsSQL } from '../../../server/lib/query_es_sql';
/* eslint-enable */
import { ExpressionValueFilter } from '../../../types';
import { getFunctionHelp } from '../../../i18n';
interface Arguments {
query: string;
count: number;
timezone: string;
}
export function essql(): ExpressionFunctionDefinition<
'essql',
ExpressionValueFilter,
Arguments,
any
> {
const { help, args: argHelp } = getFunctionHelp().essql;
return {
name: 'essql',
type: 'datatable',
context: {
types: ['filter'],
},
help,
args: {
query: {
aliases: ['_', 'q'],
types: ['string'],
help: argHelp.query,
},
count: {
types: ['number'],
help: argHelp.count,
default: 1000,
},
timezone: {
aliases: ['tz'],
types: ['string'],
default: 'UTC',
help: argHelp.timezone,
},
},
fn: (input, args, context) => {
return queryEsSQL(((context as any) as { elasticsearchClient: any }).elasticsearchClient, {
...args,
filter: input.and,
});
},
};
}

View file

@ -6,9 +6,6 @@
*/
import { demodata } from './demodata';
import { escount } from './escount';
import { esdocs } from './esdocs';
import { pointseries } from './pointseries';
import { essql } from './essql';
export const functions = [demodata, esdocs, escount, essql, pointseries];
export const functions = [demodata, pointseries];

View file

@ -24,10 +24,6 @@ export const expressionsServiceFactory: CanvasServiceFactory<ExpressionsService>
const loadServerFunctionWrappers = async () => {
if (!cached) {
cached = (async () => {
const labService = startPlugins.presentationUtil.labsService;
const hasDataSearch = labService.isProjectEnabled('labs:canvas:useDataService');
const dataSearchFns = ['essql', 'esdocs', 'escount'];
const serverFunctionList = await coreSetup.http.get(API_ROUTE_FUNCTIONS);
const batchedFunction = bfetch.batchedFunction({ url: API_ROUTE_FUNCTIONS });
const { serialize } = serializeProvider(expressions.getTypes());
@ -36,13 +32,7 @@ export const expressionsServiceFactory: CanvasServiceFactory<ExpressionsService>
// function that matches its definition, but which simply
// calls the server-side function endpoint.
Object.keys(serverFunctionList).forEach((functionName) => {
// Allow function to be overwritten if we want to use
// the server-hosted essql, esdocs, and escount functions
if (dataSearchFns.includes(functionName)) {
if (hasDataSearch && expressions.getFunction(functionName)) {
return;
}
} else if (expressions.getFunction(functionName)) {
if (expressions.getFunction(functionName)) {
return;
}

View file

@ -0,0 +1,165 @@
/*
* 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; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { essqlSearchStrategyProvider } from './essql_strategy';
import { EssqlSearchStrategyRequest } from '../../types';
import { zipObject } from 'lodash';
const getMockEssqlResponse = () => ({
body: {
columns: [
{ name: 'One', type: 'keyword' },
{ name: 'Two', type: 'keyword' },
],
rows: [
['foo', 'bar'],
['buz', 'baz'],
['beep', 'boop'],
],
cursor: 'cursor-value',
},
statusCode: 200,
});
const basicReq: EssqlSearchStrategyRequest = {
query: 'SELECT * FROM my_index;',
count: 3,
params: ['my_var'],
filter: [
{
type: 'filter',
filterType: 'exactly',
value: 'Test Value',
column: 'One',
and: [],
},
],
timezone: 'UTC',
};
describe('ESSQL search strategy', () => {
describe('strategy interface', () => {
it('returns a strategy with a `search` function', async () => {
const essqlSearch = await essqlSearchStrategyProvider();
expect(typeof essqlSearch.search).toBe('function');
});
});
describe('search()', () => {
let mockQuery: jest.Mock;
let mockClearCursor: jest.Mock;
let mockDeps: any;
beforeEach(() => {
mockQuery = jest.fn().mockResolvedValueOnce(getMockEssqlResponse());
mockClearCursor = jest.fn();
mockDeps = ({
esClient: {
asCurrentUser: {
sql: {
query: mockQuery,
clearCursor: mockClearCursor,
},
},
},
} as unknown) as any;
});
describe('query functionality', () => {
it('performs a simple query', async () => {
const sqlSearch = await essqlSearchStrategyProvider();
const result = await sqlSearch.search(basicReq, {}, mockDeps).toPromise();
const [[request]] = mockQuery.mock.calls;
expect(request.format).toEqual('json');
expect(request.body).toEqual(
expect.objectContaining({
query: basicReq.query,
client_id: 'canvas',
fetch_size: basicReq.count,
time_zone: basicReq.timezone,
field_multi_value_leniency: true,
params: ['my_var'],
})
);
const expectedColumns = getMockEssqlResponse().body.columns.map((c) => ({
id: c.name,
name: c.name,
meta: { type: 'string' },
}));
const columnNames = expectedColumns.map((c) => c.name);
const expectedRows = getMockEssqlResponse().body.rows.map((r) => zipObject(columnNames, r));
expect(result.columns).toEqual(expectedColumns);
expect(result.rows).toEqual(expectedRows);
});
it('iterates over cursor to retrieve for records query', async () => {
const pageOne = {
body: {
columns: [
{ name: 'One', type: 'keyword' },
{ name: 'Two', type: 'keyword' },
],
rows: [['foo', 'bar']],
cursor: 'cursor-value',
},
};
const pageTwo = {
body: {
rows: [['buz', 'baz']],
},
};
mockQuery.mockReset().mockReturnValueOnce(pageOne).mockReturnValueOnce(pageTwo);
const sqlSearch = await essqlSearchStrategyProvider();
const result = await sqlSearch.search({ ...basicReq, count: 2 }, {}, mockDeps).toPromise();
expect(result.rows).toHaveLength(2);
});
it('closes any cursors that remain open', async () => {
const sqlSearch = await essqlSearchStrategyProvider();
await sqlSearch.search(basicReq, {}, mockDeps).toPromise();
const [[cursorReq]] = mockClearCursor.mock.calls;
expect(cursorReq.body.cursor).toEqual('cursor-value');
});
it('emits an error if the client throws', async () => {
const req: EssqlSearchStrategyRequest = {
query: 'SELECT * FROM my_index;',
count: 1,
params: [],
filter: [
{
type: 'filter',
filterType: 'exactly',
value: 'Test Value',
column: 'category.keyword',
and: [],
},
],
timezone: 'UTC',
};
expect.assertions(1);
mockQuery.mockReset().mockRejectedValueOnce(new Error('client error'));
const eqlSearch = await essqlSearchStrategyProvider();
eqlSearch.search(req, {}, mockDeps).subscribe(
() => {},
(err) => {
expect(err).toEqual(new Error('client error'));
}
);
});
});
});
});

View file

@ -8,7 +8,7 @@
import { from } from 'rxjs';
import { map, zipObject } from 'lodash';
import { ISearchStrategy, PluginStart } from 'src/plugins/data/server';
import { ISearchStrategy } from 'src/plugins/data/server';
import { getKbnServerError } from '../../../../../src/plugins/kibana_utils/server';
import { EssqlSearchStrategyRequest, EssqlSearchStrategyResponse } from '../../types';
@ -17,9 +17,10 @@ import { buildBoolArray } from '../../common/lib/request/build_bool_array';
import { sanitizeName } from '../../common/lib/request/sanitize_name';
import { normalizeType } from '../../common/lib/request/normalize_type';
export const essqlSearchStrategyProvider = (
data: PluginStart
): ISearchStrategy<EssqlSearchStrategyRequest, EssqlSearchStrategyResponse> => {
export const essqlSearchStrategyProvider = (): ISearchStrategy<
EssqlSearchStrategyRequest,
EssqlSearchStrategyResponse
> => {
return {
search: (request, options, { esClient }) => {
const { count, query, filter, timezone, params } = request;

View file

@ -1,111 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { zipObject } from 'lodash';
import { queryEsSQL } from './query_es_sql';
// @ts-expect-error
import { buildBoolArray } from './build_bool_array';
const response = {
columns: [
{ name: 'One', type: 'keyword' },
{ name: 'Two', type: 'keyword' },
],
rows: [
['foo', 'bar'],
['buz', 'baz'],
],
cursor: 'cursor-value',
};
const baseArgs = {
count: 1,
query: 'query',
filter: [],
timezone: 'timezone',
};
const getApi = (resp = response) => {
const api = jest.fn();
api.mockResolvedValue(resp);
return api;
};
describe('query_es_sql', () => {
it('should call the api with the given args', async () => {
const api = getApi();
queryEsSQL(api, baseArgs);
expect(api).toHaveBeenCalled();
const givenArgs = api.mock.calls[0][1];
expect(givenArgs.body.fetch_size).toBe(baseArgs.count);
expect(givenArgs.body.query).toBe(baseArgs.query);
expect(givenArgs.body.time_zone).toBe(baseArgs.timezone);
});
it('formats the response', async () => {
const api = getApi();
const result = await queryEsSQL(api, baseArgs);
const expectedColumns = response.columns.map((c) => ({
id: c.name,
name: c.name,
meta: { type: 'string' },
}));
const columnNames = expectedColumns.map((c) => c.name);
const expectedRows = response.rows.map((r) => zipObject(columnNames, r));
expect(result.type).toBe('datatable');
expect(result.columns).toEqual(expectedColumns);
expect(result.rows).toEqual(expectedRows);
});
it('fetches pages until it has the requested count', async () => {
const pageOne = {
columns: [
{ name: 'One', type: 'keyword' },
{ name: 'Two', type: 'keyword' },
],
rows: [['foo', 'bar']],
cursor: 'cursor-value',
};
const pageTwo = {
rows: [['buz', 'baz']],
};
const api = getApi(pageOne);
api.mockReturnValueOnce(pageOne).mockReturnValueOnce(pageTwo);
const result = await queryEsSQL(api, { ...baseArgs, count: 2 });
expect(result.rows).toHaveLength(2);
});
it('closes any cursors that remain open', async () => {
const api = getApi();
await queryEsSQL(api, baseArgs);
expect(api.mock.calls[1][1].body.cursor).toBe(response.cursor);
});
it('throws on errors', async () => {
const api = getApi();
api.mockRejectedValueOnce(new Error('parsing_exception'));
api.mockRejectedValueOnce(new Error('generic es error'));
expect(queryEsSQL(api, baseArgs)).rejects.toThrowErrorMatchingInlineSnapshot(
`"Couldn't parse Elasticsearch SQL query. You may need to add double quotes to names containing special characters. Check your query and try again. Error: parsing_exception"`
);
expect(queryEsSQL(api, baseArgs)).rejects.toThrowErrorMatchingInlineSnapshot(
`"Unexpected error from Elasticsearch: generic es error"`
);
});
});

View file

@ -1,105 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { map, zipObject } from 'lodash';
import { buildBoolArray } from '../../common/lib/request/build_bool_array';
import { sanitizeName } from '../../common/lib/request/sanitize_name';
import { normalizeType } from '../../common/lib/request/normalize_type';
import { LegacyAPICaller } from '../../../../../src/core/server';
import { ExpressionValueFilter } from '../../types';
interface Args {
count: number;
query: string;
timezone?: string;
filter: ExpressionValueFilter[];
}
interface CursorResponse {
cursor?: string;
rows: string[][];
}
type QueryResponse = CursorResponse & {
columns: Array<{
name: string;
type: string;
}>;
cursor?: string;
rows: string[][];
};
export const queryEsSQL = async (
elasticsearchClient: LegacyAPICaller,
{ count, query, filter, timezone }: Args
) => {
try {
let response: QueryResponse = await elasticsearchClient<QueryResponse>('transport.request', {
path: '/_sql?format=json',
method: 'POST',
body: {
query,
time_zone: timezone,
fetch_size: count,
client_id: 'canvas',
filter: {
bool: {
must: [{ match_all: {} }, ...buildBoolArray(filter)],
},
},
},
});
const columns = response.columns.map(({ name, type }) => {
return {
id: sanitizeName(name),
name: sanitizeName(name),
meta: { type: normalizeType(type) },
};
});
const columnNames = map(columns, 'name');
let rows = response.rows.map((row) => zipObject(columnNames, row));
while (rows.length < count && response.cursor !== undefined) {
response = await elasticsearchClient<QueryResponse>('transport.request', {
path: '/_sql?format=json',
method: 'POST',
body: {
cursor: response.cursor,
},
});
rows = [...rows, ...response.rows.map((row) => zipObject(columnNames, row))];
}
if (response.cursor !== undefined) {
elasticsearchClient('transport.request', {
path: '/_sql/close',
method: 'POST',
body: {
cursor: response.cursor,
},
});
}
return {
type: 'datatable',
meta: {
type: 'essql',
},
columns,
rows,
};
} catch (e) {
if (e.message.indexOf('parsing_exception') > -1) {
throw new Error(
`Couldn't parse Elasticsearch SQL query. You may need to add double quotes to names containing special characters. Check your query and try again. Error: ${e.message}`
);
}
throw new Error(`Unexpected error from Elasticsearch: ${e.message}`);
}
};

View file

@ -61,7 +61,6 @@ export class CanvasPlugin implements Plugin {
router: canvasRouter,
expressions: plugins.expressions,
bfetch: plugins.bfetch,
elasticsearch: coreSetup.elasticsearch,
logger: this.logger,
});
@ -77,7 +76,7 @@ export class CanvasPlugin implements Plugin {
setupInterpreter(plugins.expressions);
coreSetup.getStartServices().then(([_, depsStart]) => {
const strategy = essqlSearchStrategyProvider(depsStart.data);
const strategy = essqlSearchStrategyProvider();
plugins.data.search.registerSearchStrategy(ESSQL_SEARCH_STRATEGY, strategy);
});
}

View file

@ -13,7 +13,7 @@ import { getMockedRouterDeps } from '../test_helpers';
const mockRouteContext = ({
core: {
elasticsearch: {
legacy: { client: elasticsearchServiceMock.createLegacyScopedClusterClient() },
client: elasticsearchServiceMock.createScopedClusterClient(),
},
},
} as unknown) as RequestHandlerContext;
@ -33,27 +33,29 @@ describe('Retrieve ES Fields', () => {
it(`returns 200 with fields from existing index/index pattern`, async () => {
const index = 'test';
const mockResults = {
indices: ['test'],
fields: {
'@timestamp': {
date: {
type: 'date',
searchable: true,
aggregatable: true,
body: {
indices: ['test'],
fields: {
'@timestamp': {
date: {
type: 'date',
searchable: true,
aggregatable: true,
},
},
},
name: {
text: {
type: 'text',
searchable: true,
aggregatable: false,
name: {
text: {
type: 'text',
searchable: true,
aggregatable: false,
},
},
},
products: {
object: {
type: 'object',
searchable: false,
aggregatable: false,
products: {
object: {
type: 'object',
searchable: false,
aggregatable: false,
},
},
},
},
@ -66,10 +68,10 @@ describe('Retrieve ES Fields', () => {
},
});
const callAsCurrentUserMock = mockRouteContext.core.elasticsearch.legacy.client
.callAsCurrentUser as jest.Mock;
const fieldCapsMock = mockRouteContext.core.elasticsearch.client.asCurrentUser
.fieldCaps as jest.Mock;
callAsCurrentUserMock.mockResolvedValueOnce(mockResults);
fieldCapsMock.mockResolvedValueOnce(mockResults);
const response = await routeHandler(mockRouteContext, request, kibanaResponseFactory);
@ -85,7 +87,7 @@ describe('Retrieve ES Fields', () => {
it(`returns 200 with empty object when index/index pattern has no fields`, async () => {
const index = 'test';
const mockResults = { indices: [index], fields: {} };
const mockResults = { body: { indices: [index], fields: {} } };
const request = httpServerMock.createKibanaRequest({
method: 'get',
path,
@ -94,10 +96,10 @@ describe('Retrieve ES Fields', () => {
},
});
const callAsCurrentUserMock = mockRouteContext.core.elasticsearch.legacy.client
.callAsCurrentUser as jest.Mock;
const fieldCapsMock = mockRouteContext.core.elasticsearch.client.asCurrentUser
.fieldCaps as jest.Mock;
callAsCurrentUserMock.mockResolvedValueOnce(mockResults);
fieldCapsMock.mockResolvedValueOnce(mockResults);
const response = await routeHandler(mockRouteContext, request, kibanaResponseFactory);
@ -109,8 +111,10 @@ describe('Retrieve ES Fields', () => {
const index = 'test';
const mockResults = {
indices: [index],
fields: {},
body: {
indices: [index],
fields: {},
},
};
const request = httpServerMock.createKibanaRequest({
@ -122,10 +126,10 @@ describe('Retrieve ES Fields', () => {
},
});
const callAsCurrentUserMock = mockRouteContext.core.elasticsearch.legacy.client
.callAsCurrentUser as jest.Mock;
const fieldCapsMock = mockRouteContext.core.elasticsearch.client.asCurrentUser
.fieldCaps as jest.Mock;
callAsCurrentUserMock.mockResolvedValueOnce(mockResults);
fieldCapsMock.mockResolvedValueOnce(mockResults);
const response = await routeHandler(mockRouteContext, request, kibanaResponseFactory);
@ -142,10 +146,10 @@ describe('Retrieve ES Fields', () => {
},
});
const callAsCurrentUserMock = mockRouteContext.core.elasticsearch.legacy.client
.callAsCurrentUser as jest.Mock;
const fieldCapsMock = mockRouteContext.core.elasticsearch.client.asCurrentUser
.fieldCaps as jest.Mock;
callAsCurrentUserMock.mockRejectedValueOnce(new Error('Index not found'));
fieldCapsMock.mockRejectedValueOnce(new Error('Index not found'));
await expect(
routeHandler(mockRouteContext, request, kibanaResponseFactory)

View file

@ -28,7 +28,7 @@ export function initializeESFieldsRoute(deps: RouteInitializerDeps) {
},
},
catchErrorHandler(async (context, request, response) => {
const { callAsCurrentUser } = context.core.elasticsearch.legacy.client;
const client = context.core.elasticsearch.client.asCurrentUser;
const { index, fields } = request.query;
const config = {
@ -36,8 +36,8 @@ export function initializeESFieldsRoute(deps: RouteInitializerDeps) {
fields: fields || '*',
};
const esFields = await callAsCurrentUser('fieldCaps', config).then((resp) => {
return mapValues(resp.fields, (types) => {
const esFields = await client.fieldCaps(config).then((resp) => {
return mapValues(resp.body.fields, (types) => {
if (keys(types).length > 1) {
return 'conflict';
}

View file

@ -5,7 +5,6 @@
* 2.0.
*/
import { LegacyAPICaller } from 'src/core/server';
import { serializeProvider } from '../../../../../../src/plugins/expressions/common';
import { RouteInitializerDeps } from '../';
import { API_ROUTE_FUNCTIONS } from '../../../common/lib/constants';
@ -34,12 +33,9 @@ export function initializeGetFunctionsRoute(deps: RouteInitializerDeps) {
}
export function initializeBatchFunctionsRoute(deps: RouteInitializerDeps) {
const { bfetch, elasticsearch, expressions } = deps;
const { bfetch, expressions } = deps;
async function runFunction(
handlers: { environment: string; elasticsearchClient: LegacyAPICaller },
fnCall: FunctionCall
) {
async function runFunction(handlers: { environment: string }, fnCall: FunctionCall) {
const { functionName, args, context } = fnCall;
const { deserialize } = serializeProvider(expressions.getTypes());
@ -61,7 +57,6 @@ export function initializeBatchFunctionsRoute(deps: RouteInitializerDeps) {
onBatchItem: async (fnCall: FunctionCall) => {
const handlers = {
environment: 'server',
elasticsearchClient: elasticsearch.legacy.client.asScoped(request).callAsCurrentUser,
};
const result = await runFunction(handlers, fnCall);
if (typeof result === 'undefined') {

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import { IRouter, Logger, ElasticsearchServiceSetup } from 'src/core/server';
import { IRouter, Logger } from 'src/core/server';
import { ExpressionsServerSetup } from 'src/plugins/expressions/server';
import { BfetchServerSetup } from 'src/plugins/bfetch/server';
import { initCustomElementsRoutes } from './custom_elements';
@ -20,7 +20,6 @@ export interface RouteInitializerDeps {
logger: Logger;
expressions: ExpressionsServerSetup;
bfetch: BfetchServerSetup;
elasticsearch: ElasticsearchServiceSetup;
}
export function initRoutes(deps: RouteInitializerDeps) {