mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[Search Application] Use ES JS client instead of Transport (#158446)
- ✔️ Update es client version - ✔️ Replace transport api to es client - ✔️ Update tests - ✔️ Fix TS issues
This commit is contained in:
parent
76e36beead
commit
815fddb9f0
46 changed files with 129 additions and 169 deletions
|
@ -96,7 +96,7 @@
|
|||
"@elastic/apm-rum-react": "^1.4.2",
|
||||
"@elastic/charts": "55.0.0",
|
||||
"@elastic/datemath": "5.0.3",
|
||||
"@elastic/elasticsearch": "npm:@elastic/elasticsearch-canary@8.6.0-canary.3",
|
||||
"@elastic/elasticsearch": "npm:@elastic/elasticsearch-canary@8.8.0-canary.2",
|
||||
"@elastic/ems-client": "8.4.0",
|
||||
"@elastic/eui": "80.0.0",
|
||||
"@elastic/filesaver": "1.1.2",
|
||||
|
|
|
@ -57,7 +57,7 @@ export function getShouldClauses(significantTerms: SignificantTerm[]) {
|
|||
export function getFrequentItemSetsAggFields(significantTerms: SignificantTerm[]) {
|
||||
return Array.from(
|
||||
group(significantTerms, ({ fieldName }) => fieldName),
|
||||
([field, values]) => ({ field, include: values.map((d) => d.fieldValue) })
|
||||
([field, values]) => ({ field, include: values.map((d) => String(d.fieldValue)) })
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -100,7 +100,6 @@ export async function fetchFrequentItemSets(
|
|||
|
||||
const frequentItemSetsAgg: Record<string, estypes.AggregationsAggregationContainer> = {
|
||||
fi: {
|
||||
// @ts-expect-error `frequent_item_sets` is not yet part of `AggregationsAggregationContainer`
|
||||
frequent_item_sets: {
|
||||
minimum_set_size: 2,
|
||||
size: 200,
|
||||
|
|
|
@ -57,7 +57,7 @@ export const generateMlInferencePipelineBody = ({
|
|||
model,
|
||||
pipelineName,
|
||||
}: MlInferencePipelineParams): MlInferencePipeline => {
|
||||
const inferenceType = Object.keys(model.inference_config)[0];
|
||||
const inferenceType = Object.keys(model.inference_config || {})[0];
|
||||
const pipelineDefinition: MlInferencePipeline = {
|
||||
description: description ?? '',
|
||||
processors: [],
|
||||
|
|
|
@ -53,7 +53,7 @@ export const NLP_DISPLAY_TITLES: Record<string, string | undefined> = {
|
|||
|
||||
export const isSupportedMLModel = (model: TrainedModelConfigResponse): boolean => {
|
||||
return (
|
||||
Object.keys(model.inference_config).some((key) => NLP_CONFIG_KEYS.includes(key)) ||
|
||||
Object.keys(model.inference_config || {}).some((key) => NLP_CONFIG_KEYS.includes(key)) ||
|
||||
model.model_type === TRAINED_MODEL_TYPE.LANG_IDENT
|
||||
);
|
||||
};
|
||||
|
@ -83,7 +83,8 @@ export const getMLType = (modelTypes: string[]): string => {
|
|||
|
||||
export const getModelDisplayTitle = (type: string): string | undefined => NLP_DISPLAY_TITLES[type];
|
||||
|
||||
export const isTextExpansionModel = (model: TrainedModel) => model.inference_config.text_expansion;
|
||||
export const isTextExpansionModel = (model: TrainedModel): boolean =>
|
||||
Boolean(model.inference_config?.text_expansion);
|
||||
|
||||
/**
|
||||
* Sort function for displaying a list of models. Promotes text_expansion models and sorts the rest by model ID.
|
||||
|
|
|
@ -18,8 +18,8 @@ jest.mock('./fetch_analytics_collection', () => ({ fetchAnalyticsCollections: je
|
|||
describe('add analytics collection lib function', () => {
|
||||
const mockClient = {
|
||||
asCurrentUser: {
|
||||
transport: {
|
||||
request: jest.fn(),
|
||||
searchApplication: {
|
||||
putBehavioralAnalytics: jest.fn(),
|
||||
},
|
||||
},
|
||||
asInternalUser: {},
|
||||
|
@ -34,7 +34,7 @@ describe('add analytics collection lib function', () => {
|
|||
});
|
||||
|
||||
it('should add analytics collection', async () => {
|
||||
mockClient.asCurrentUser.transport.request.mockImplementation(() => ({
|
||||
mockClient.asCurrentUser.searchApplication.putBehavioralAnalytics.mockImplementation(() => ({
|
||||
acknowledged: true,
|
||||
name: `example`,
|
||||
}));
|
||||
|
@ -57,9 +57,8 @@ describe('add analytics collection lib function', () => {
|
|||
name: 'example',
|
||||
});
|
||||
|
||||
expect(mockClient.asCurrentUser.transport.request).toHaveBeenCalledWith({
|
||||
method: 'PUT',
|
||||
path: '/_application/analytics/example',
|
||||
expect(mockClient.asCurrentUser.searchApplication.putBehavioralAnalytics).toHaveBeenCalledWith({
|
||||
name: 'example',
|
||||
});
|
||||
|
||||
expect(mockDataViewsService.createAndSave).toHaveBeenCalledWith(
|
||||
|
@ -74,7 +73,7 @@ describe('add analytics collection lib function', () => {
|
|||
});
|
||||
|
||||
it('should reject if analytics collection already exists', async () => {
|
||||
mockClient.asCurrentUser.transport.request.mockImplementation(() =>
|
||||
mockClient.asCurrentUser.searchApplication.putBehavioralAnalytics.mockImplementation(() =>
|
||||
Promise.reject({
|
||||
meta: {
|
||||
body: {
|
||||
|
|
|
@ -22,14 +22,10 @@ interface CollectionsPutResponse {
|
|||
const createAnalyticsCollection = async (
|
||||
client: IScopedClusterClient,
|
||||
name: string
|
||||
): Promise<CollectionsPutResponse> => {
|
||||
const response = await client.asCurrentUser.transport.request<CollectionsPutResponse>({
|
||||
method: 'PUT',
|
||||
path: `/_application/analytics/${name}`,
|
||||
});
|
||||
|
||||
return response;
|
||||
};
|
||||
): Promise<CollectionsPutResponse> =>
|
||||
(await client.asCurrentUser.searchApplication.putBehavioralAnalytics({
|
||||
name,
|
||||
})) as CollectionsPutResponse;
|
||||
|
||||
const createDataView = async (
|
||||
dataViewsService: DataViewsService,
|
||||
|
|
|
@ -14,8 +14,8 @@ import { deleteAnalyticsCollectionById } from './delete_analytics_collection';
|
|||
describe('delete analytics collection lib function', () => {
|
||||
const mockClient = {
|
||||
asCurrentUser: {
|
||||
transport: {
|
||||
request: jest.fn(),
|
||||
searchApplication: {
|
||||
deleteBehavioralAnalytics: jest.fn(),
|
||||
},
|
||||
},
|
||||
asInternalUser: {},
|
||||
|
@ -31,14 +31,15 @@ describe('delete analytics collection lib function', () => {
|
|||
deleteAnalyticsCollectionById(mockClient as unknown as IScopedClusterClient, 'example')
|
||||
).resolves.toBeUndefined();
|
||||
|
||||
expect(mockClient.asCurrentUser.transport.request).toHaveBeenCalledWith({
|
||||
method: 'DELETE',
|
||||
path: '/_application/analytics/example',
|
||||
expect(
|
||||
mockClient.asCurrentUser.searchApplication.deleteBehavioralAnalytics
|
||||
).toHaveBeenCalledWith({
|
||||
name: 'example',
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw an exception when analytics collection does not exist', async () => {
|
||||
mockClient.asCurrentUser.transport.request.mockImplementation(() =>
|
||||
mockClient.asCurrentUser.searchApplication.deleteBehavioralAnalytics.mockImplementation(() =>
|
||||
Promise.reject({
|
||||
meta: {
|
||||
body: {
|
||||
|
|
|
@ -10,16 +10,9 @@ import { IScopedClusterClient } from '@kbn/core-elasticsearch-server';
|
|||
import { ErrorCode } from '../../../common/types/error_codes';
|
||||
import { isResourceNotFoundException } from '../../utils/identify_exceptions';
|
||||
|
||||
interface CollectionsDeleteResponse {
|
||||
acknowledged: boolean;
|
||||
}
|
||||
|
||||
export const deleteAnalyticsCollectionById = async (client: IScopedClusterClient, name: string) => {
|
||||
try {
|
||||
await client.asCurrentUser.transport.request<CollectionsDeleteResponse>({
|
||||
method: 'DELETE',
|
||||
path: `/_application/analytics/${name}`,
|
||||
});
|
||||
await client.asCurrentUser.searchApplication.deleteBehavioralAnalytics({ name });
|
||||
} catch (error) {
|
||||
if (isResourceNotFoundException(error)) {
|
||||
throw new Error(ErrorCode.ANALYTICS_COLLECTION_NOT_FOUND);
|
||||
|
|
|
@ -12,8 +12,8 @@ import { fetchAnalyticsCollections } from './fetch_analytics_collection';
|
|||
describe('fetch analytics collection lib function', () => {
|
||||
const mockClient = {
|
||||
asCurrentUser: {
|
||||
transport: {
|
||||
request: jest.fn(),
|
||||
searchApplication: {
|
||||
getBehavioralAnalytics: jest.fn(),
|
||||
},
|
||||
},
|
||||
asInternalUser: {},
|
||||
|
@ -25,7 +25,7 @@ describe('fetch analytics collection lib function', () => {
|
|||
|
||||
describe('fetch collections', () => {
|
||||
it('should return a list of analytics collections', async () => {
|
||||
mockClient.asCurrentUser.transport.request.mockImplementation(() =>
|
||||
mockClient.asCurrentUser.searchApplication.getBehavioralAnalytics.mockImplementation(() =>
|
||||
Promise.resolve({
|
||||
example: {
|
||||
event_data_stream: {
|
||||
|
@ -50,7 +50,7 @@ describe('fetch analytics collection lib function', () => {
|
|||
|
||||
describe('fetch collection by Id', () => {
|
||||
it('should fetch analytics collection by Id', async () => {
|
||||
mockClient.asCurrentUser.transport.request.mockImplementation(() =>
|
||||
mockClient.asCurrentUser.searchApplication.getBehavioralAnalytics.mockImplementation(() =>
|
||||
Promise.resolve({
|
||||
example: {
|
||||
event_data_stream: {
|
||||
|
|
|
@ -12,22 +12,13 @@ import { ErrorCode } from '../../../common/types/error_codes';
|
|||
|
||||
import { isResourceNotFoundException } from '../../utils/identify_exceptions';
|
||||
|
||||
interface CollectionsListResponse {
|
||||
[name: string]: {
|
||||
event_data_stream: {
|
||||
name: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export const fetchAnalyticsCollections = async (
|
||||
client: IScopedClusterClient,
|
||||
query: string = ''
|
||||
): Promise<AnalyticsCollection[]> => {
|
||||
try {
|
||||
const collections = await client.asCurrentUser.transport.request<CollectionsListResponse>({
|
||||
method: 'GET',
|
||||
path: `/_application/analytics/${query}`,
|
||||
const collections = await client.asCurrentUser.searchApplication.getBehavioralAnalytics({
|
||||
name: query,
|
||||
});
|
||||
|
||||
return Object.keys(collections).map((value) => {
|
||||
|
|
|
@ -27,10 +27,7 @@ export const getMlModelDeploymentStatus = async (
|
|||
throw new Error('Machine Learning is not enabled');
|
||||
}
|
||||
|
||||
// TODO: the ts-expect-error below should be removed once the correct typings are
|
||||
// available in Kibana
|
||||
const modelDetailsRequest: MlGetTrainedModelsRequest = {
|
||||
// @ts-expect-error @elastic-elasticsearch getTrainedModels types incorrect
|
||||
include: 'definition_status',
|
||||
model_id: modelName,
|
||||
};
|
||||
|
@ -43,8 +40,6 @@ export const getMlModelDeploymentStatus = async (
|
|||
return getDefaultStatusReturn(MlModelDeploymentState.NotDeployed, modelName);
|
||||
}
|
||||
|
||||
// TODO - we can remove this cast to the extension once the new types are available
|
||||
// in kibana that includes the fully_defined field
|
||||
const firstTrainedModelConfig = modelDetailsResponse.trained_model_configs
|
||||
? (modelDetailsResponse.trained_model_configs[0] as MlTrainedModelConfigWithDefined)
|
||||
: (undefined as unknown as MlTrainedModelConfigWithDefined);
|
||||
|
|
|
@ -26,8 +26,8 @@ describe('engines routes', () => {
|
|||
|
||||
const mockClient = {
|
||||
asCurrentUser: {
|
||||
transport: {
|
||||
request: jest.fn(),
|
||||
searchApplication: {
|
||||
list: jest.fn(),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -49,14 +49,10 @@ describe('engines routes', () => {
|
|||
});
|
||||
|
||||
it('GET search applications API creates request', async () => {
|
||||
mockClient.asCurrentUser.transport.request.mockImplementation(() => ({}));
|
||||
mockClient.asCurrentUser.searchApplication.list.mockImplementation(() => ({}));
|
||||
const request = { query: {} };
|
||||
await mockRouter.callRoute({});
|
||||
expect(mockClient.asCurrentUser.transport.request).toHaveBeenCalledWith({
|
||||
method: 'GET',
|
||||
path: '/_application/search_application',
|
||||
querystring: request.query,
|
||||
});
|
||||
expect(mockClient.asCurrentUser.searchApplication.list).toHaveBeenCalledWith(request.query);
|
||||
expect(mockRouter.response.ok).toHaveBeenCalledWith({
|
||||
body: {},
|
||||
});
|
||||
|
@ -85,8 +81,8 @@ describe('engines routes', () => {
|
|||
let mockRouter: MockRouter;
|
||||
const mockClient = {
|
||||
asCurrentUser: {
|
||||
transport: {
|
||||
request: jest.fn(),
|
||||
searchApplication: {
|
||||
get: jest.fn(),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -109,14 +105,13 @@ describe('engines routes', () => {
|
|||
});
|
||||
|
||||
it('GET search application API creates request', async () => {
|
||||
mockClient.asCurrentUser.transport.request.mockImplementation(() => ({}));
|
||||
mockClient.asCurrentUser.searchApplication.get.mockImplementation(() => ({}));
|
||||
await mockRouter.callRoute({
|
||||
params: { engine_name: 'engine-name' },
|
||||
});
|
||||
|
||||
expect(mockClient.asCurrentUser.transport.request).toHaveBeenCalledWith({
|
||||
method: 'GET',
|
||||
path: '/_application/search_application/engine-name',
|
||||
expect(mockClient.asCurrentUser.searchApplication.get).toHaveBeenCalledWith({
|
||||
name: 'engine-name',
|
||||
});
|
||||
const mock = jest.fn();
|
||||
|
||||
|
@ -156,8 +151,8 @@ describe('engines routes', () => {
|
|||
let mockRouter: MockRouter;
|
||||
const mockClient = {
|
||||
asCurrentUser: {
|
||||
transport: {
|
||||
request: jest.fn(),
|
||||
searchApplication: {
|
||||
put: jest.fn(),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -180,7 +175,7 @@ describe('engines routes', () => {
|
|||
});
|
||||
|
||||
it('PUT - Upsert API creates request - create', async () => {
|
||||
mockClient.asCurrentUser.transport.request.mockImplementation(() => ({
|
||||
mockClient.asCurrentUser.searchApplication.put.mockImplementation(() => ({
|
||||
acknowledged: true,
|
||||
}));
|
||||
|
||||
|
@ -193,13 +188,14 @@ describe('engines routes', () => {
|
|||
},
|
||||
query: { create: true },
|
||||
});
|
||||
expect(mockClient.asCurrentUser.transport.request).toHaveBeenCalledWith({
|
||||
body: {
|
||||
expect(mockClient.asCurrentUser.searchApplication.put).toHaveBeenCalledWith({
|
||||
create: true,
|
||||
name: 'engine-name',
|
||||
search_application: {
|
||||
indices: ['test-indices-1'],
|
||||
name: 'engine-name',
|
||||
updated_at_millis: expect.any(Number),
|
||||
},
|
||||
method: 'PUT',
|
||||
path: '/_application/search_application/engine-name',
|
||||
querystring: { create: true },
|
||||
});
|
||||
const mock = jest.fn();
|
||||
const mockResponse = mock({ result: 'created' });
|
||||
|
@ -211,7 +207,7 @@ describe('engines routes', () => {
|
|||
});
|
||||
});
|
||||
it('PUT - Upsert API creates request - update', async () => {
|
||||
mockClient.asCurrentUser.transport.request.mockImplementation(() => ({
|
||||
mockClient.asCurrentUser.searchApplication.put.mockImplementation(() => ({
|
||||
acknowledged: true,
|
||||
}));
|
||||
|
||||
|
@ -223,13 +219,13 @@ describe('engines routes', () => {
|
|||
engine_name: 'engine-name',
|
||||
},
|
||||
});
|
||||
expect(mockClient.asCurrentUser.transport.request).toHaveBeenCalledWith({
|
||||
body: {
|
||||
expect(mockClient.asCurrentUser.searchApplication.put).toHaveBeenCalledWith({
|
||||
name: 'engine-name',
|
||||
search_application: {
|
||||
indices: ['test-indices-1'],
|
||||
name: 'engine-name',
|
||||
updated_at_millis: expect.any(Number),
|
||||
},
|
||||
method: 'PUT',
|
||||
path: '/_application/search_application/engine-name',
|
||||
querystring: {},
|
||||
});
|
||||
const mock = jest.fn();
|
||||
const mockResponse = mock({ result: 'updated' });
|
||||
|
@ -268,7 +264,7 @@ describe('engines routes', () => {
|
|||
});
|
||||
|
||||
it('returns 409 when upsert create search application throws error', async () => {
|
||||
(mockClient.asCurrentUser.transport.request as jest.Mock).mockRejectedValueOnce({
|
||||
(mockClient.asCurrentUser.searchApplication.put as jest.Mock).mockRejectedValueOnce({
|
||||
meta: {
|
||||
body: {
|
||||
error: {
|
||||
|
@ -298,8 +294,8 @@ describe('engines routes', () => {
|
|||
let mockRouter: MockRouter;
|
||||
const mockClient = {
|
||||
asCurrentUser: {
|
||||
transport: {
|
||||
request: jest.fn(),
|
||||
searchApplication: {
|
||||
delete: jest.fn(),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -322,7 +318,7 @@ describe('engines routes', () => {
|
|||
});
|
||||
|
||||
it('Delete API creates request', async () => {
|
||||
mockClient.asCurrentUser.transport.request.mockImplementation(() => ({
|
||||
mockClient.asCurrentUser.searchApplication.delete.mockImplementation(() => ({
|
||||
acknowledged: true,
|
||||
}));
|
||||
|
||||
|
@ -331,9 +327,8 @@ describe('engines routes', () => {
|
|||
engine_name: 'engine-name',
|
||||
},
|
||||
});
|
||||
expect(mockClient.asCurrentUser.transport.request).toHaveBeenCalledWith({
|
||||
method: 'DELETE',
|
||||
path: '_application/search_application/engine-name',
|
||||
expect(mockClient.asCurrentUser.searchApplication.delete).toHaveBeenCalledWith({
|
||||
name: 'engine-name',
|
||||
});
|
||||
expect(mockRouter.response.ok).toHaveBeenCalledWith({
|
||||
body: {
|
||||
|
@ -448,7 +443,7 @@ describe('engines routes', () => {
|
|||
describe('GET /internal/enterprise_search/engines/{engine_name}/field_capabilities', () => {
|
||||
let mockRouter: MockRouter;
|
||||
const mockClient = {
|
||||
asCurrentUser: { transport: { request: jest.fn() } },
|
||||
asCurrentUser: { searchApplication: { get: jest.fn() } },
|
||||
};
|
||||
const mockCore = {
|
||||
elasticsearch: { client: mockClient },
|
||||
|
@ -485,16 +480,17 @@ describe('engines routes', () => {
|
|||
name: 'unit-test',
|
||||
};
|
||||
|
||||
(mockClient.asCurrentUser.transport.request as jest.Mock).mockResolvedValueOnce(engineResult);
|
||||
(mockClient.asCurrentUser.searchApplication.get as jest.Mock).mockResolvedValueOnce(
|
||||
engineResult
|
||||
);
|
||||
(fetchEngineFieldCapabilities as jest.Mock).mockResolvedValueOnce(fieldCapabilitiesResult);
|
||||
|
||||
await mockRouter.callRoute({
|
||||
params: { engine_name: 'unit-test' },
|
||||
});
|
||||
|
||||
expect(mockClient.asCurrentUser.transport.request).toHaveBeenCalledWith({
|
||||
method: 'GET',
|
||||
path: '/_application/search_application/unit-test',
|
||||
expect(mockClient.asCurrentUser.searchApplication.get).toHaveBeenCalledWith({
|
||||
name: 'unit-test',
|
||||
});
|
||||
expect(fetchEngineFieldCapabilities).toHaveBeenCalledWith(mockClient, engineResult);
|
||||
expect(mockRouter.response.ok).toHaveBeenCalledWith({
|
||||
|
@ -503,7 +499,7 @@ describe('engines routes', () => {
|
|||
});
|
||||
});
|
||||
it('returns 404 when fetch engine throws a not found exception', async () => {
|
||||
(mockClient.asCurrentUser.transport.request as jest.Mock).mockRejectedValueOnce({
|
||||
(mockClient.asCurrentUser.searchApplication.get as jest.Mock).mockRejectedValueOnce({
|
||||
meta: {
|
||||
body: {
|
||||
error: {
|
||||
|
@ -529,7 +525,7 @@ describe('engines routes', () => {
|
|||
});
|
||||
});
|
||||
it('returns error when fetch engine returns an unknown error', async () => {
|
||||
(mockClient.asCurrentUser.transport.request as jest.Mock).mockRejectedValueOnce({
|
||||
(mockClient.asCurrentUser.searchApplication.get as jest.Mock).mockRejectedValueOnce({
|
||||
body: {
|
||||
attributes: {
|
||||
error_code: 'unknown_error',
|
||||
|
|
|
@ -41,13 +41,9 @@ export function registerEnginesRoutes({ log, router }: RouteDependencies) {
|
|||
},
|
||||
elasticsearchErrorHandler(log, async (context, request, response) => {
|
||||
const { client } = (await context.core).elasticsearch;
|
||||
const engines = await client.asCurrentUser.transport.request<EnterpriseSearchEnginesResponse>(
|
||||
{
|
||||
method: 'GET',
|
||||
path: `/_application/search_application`,
|
||||
querystring: request.query,
|
||||
}
|
||||
);
|
||||
const engines = (await client.asCurrentUser.searchApplication.list(
|
||||
request.query
|
||||
)) as EnterpriseSearchEnginesResponse;
|
||||
|
||||
return response.ok({ body: engines });
|
||||
})
|
||||
|
@ -64,10 +60,9 @@ export function registerEnginesRoutes({ log, router }: RouteDependencies) {
|
|||
},
|
||||
elasticsearchErrorHandler(log, async (context, request, response) => {
|
||||
const { client } = (await context.core).elasticsearch;
|
||||
const engine = await client.asCurrentUser.transport.request<EnterpriseSearchEngine>({
|
||||
method: 'GET',
|
||||
path: `/_application/search_application/${request.params.engine_name}`,
|
||||
});
|
||||
const engine = (await client.asCurrentUser.searchApplication.get({
|
||||
name: request.params.engine_name,
|
||||
})) as EnterpriseSearchEngine;
|
||||
const indicesStats = await fetchIndicesStats(client, engine.indices);
|
||||
|
||||
return response.ok({ body: { ...engine, indices: indicesStats } });
|
||||
|
@ -93,13 +88,16 @@ export function registerEnginesRoutes({ log, router }: RouteDependencies) {
|
|||
elasticsearchErrorHandler(log, async (context, request, response) => {
|
||||
const { client } = (await context.core).elasticsearch;
|
||||
try {
|
||||
const engine =
|
||||
await client.asCurrentUser.transport.request<EnterpriseSearchEngineUpsertResponse>({
|
||||
body: { indices: request.body.indices },
|
||||
method: 'PUT',
|
||||
path: `/_application/search_application/${request.params.engine_name}`,
|
||||
querystring: request.query,
|
||||
});
|
||||
const engine = (await client.asCurrentUser.searchApplication.put({
|
||||
...request.query,
|
||||
name: request.params.engine_name,
|
||||
search_application: {
|
||||
indices: request.body.indices,
|
||||
name: request.params.engine_name,
|
||||
updated_at_millis: Date.now(),
|
||||
},
|
||||
})) as EnterpriseSearchEngineUpsertResponse;
|
||||
|
||||
return response.ok({ body: engine });
|
||||
} catch (error) {
|
||||
if (isVersionConflictEngineException(error)) {
|
||||
|
@ -132,10 +130,10 @@ export function registerEnginesRoutes({ log, router }: RouteDependencies) {
|
|||
},
|
||||
elasticsearchErrorHandler(log, async (context, request, response) => {
|
||||
const { client } = (await context.core).elasticsearch;
|
||||
const engine = await client.asCurrentUser.transport.request<AcknowledgedResponseBase>({
|
||||
method: 'DELETE',
|
||||
path: `_application/search_application/${request.params.engine_name}`,
|
||||
});
|
||||
const engine = (await client.asCurrentUser.searchApplication.delete({
|
||||
name: request.params.engine_name,
|
||||
})) as AcknowledgedResponseBase;
|
||||
|
||||
return response.ok({ body: engine });
|
||||
})
|
||||
);
|
||||
|
@ -155,8 +153,8 @@ export function registerEnginesRoutes({ log, router }: RouteDependencies) {
|
|||
elasticsearchErrorHandler(log, async (context, request, response) => {
|
||||
const { client } = (await context.core).elasticsearch;
|
||||
const engines = await client.asCurrentUser.search<SearchResponse>({
|
||||
index: request.params.engine_name,
|
||||
...request.body,
|
||||
index: request.params.engine_name,
|
||||
});
|
||||
return response.ok({ body: engines });
|
||||
})
|
||||
|
@ -193,13 +191,11 @@ export function registerEnginesRoutes({ log, router }: RouteDependencies) {
|
|||
},
|
||||
elasticsearchErrorHandler(log, async (context, request, response) => {
|
||||
try {
|
||||
const engineName = decodeURIComponent(request.params.engine_name);
|
||||
const { client } = (await context.core).elasticsearch;
|
||||
|
||||
const engine = await client.asCurrentUser.transport.request<EnterpriseSearchEngine>({
|
||||
method: 'GET',
|
||||
path: `/_application/search_application/${engineName}`,
|
||||
});
|
||||
const engine = (await client.asCurrentUser.searchApplication.get({
|
||||
name: request.params.engine_name,
|
||||
})) as EnterpriseSearchEngine;
|
||||
|
||||
const data = await fetchEngineFieldCapabilities(client, engine);
|
||||
return response.ok({
|
||||
|
|
|
@ -703,11 +703,7 @@ const updateExistingDataStream = async ({
|
|||
}
|
||||
|
||||
// Trigger a rollover if the index mode or source type has changed
|
||||
if (
|
||||
currentIndexMode !== settings?.index?.mode ||
|
||||
// @ts-expect-error Property 'mode' does not exist on type 'MappingSourceField'
|
||||
currentSourceType !== mappings?._source?.mode
|
||||
) {
|
||||
if (currentIndexMode !== settings?.index?.mode || currentSourceType !== mappings?._source?.mode) {
|
||||
logger.info(
|
||||
`Index mode or source type has changed for ${dataStreamName}, triggering a rollover`
|
||||
);
|
||||
|
|
|
@ -767,11 +767,13 @@ async function handleTransformInstall({
|
|||
{ logger, additionalResponseStatuses: [400] }
|
||||
);
|
||||
if (Array.isArray(transformStats.transforms) && transformStats.transforms.length === 1) {
|
||||
// @ts-expect-error TransformGetTransformStatsTransformStats should have 'health'
|
||||
const transformHealth = transformStats.transforms[0].health;
|
||||
if (
|
||||
transformHealth &&
|
||||
transformHealth.status === 'red' &&
|
||||
// @ts-expect-error TransformGetTransformStatsTransformStatsHealth should have 'issues'
|
||||
Array.isArray(transformHealth.issues) &&
|
||||
// @ts-expect-error TransformGetTransformStatsTransformStatsHealth should have 'issues'
|
||||
transformHealth.issues.find(
|
||||
(i: { issue: string }) => i.issue === 'Privileges check failed'
|
||||
)
|
||||
|
|
|
@ -28,6 +28,7 @@ export const callValidateIndicesAPI = async (requestArgs: RequestArgs, fetch: Ht
|
|||
const response = await fetch(LOG_ANALYSIS_VALIDATE_INDICES_PATH, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(
|
||||
// @ts-expect-error TODO: fix after elasticsearch-js bump
|
||||
validationIndicesRequestPayloadRT.encode({ data: { indices, fields, runtimeMappings } })
|
||||
),
|
||||
});
|
||||
|
|
|
@ -40,7 +40,6 @@ export const registerPrivilegesRoute = ({ router, config }: RouteDependencies) =
|
|||
|
||||
const { has_all_requested: hasAllPrivileges, cluster } =
|
||||
await clusterClient.asCurrentUser.security.hasPrivileges({
|
||||
// @ts-expect-error @elastic/elasticsearch SecurityClusterPrivilege doesn’t contain all the priviledges
|
||||
body: { cluster: APP_CLUSTER_REQUIRED_PRIVILEGES },
|
||||
});
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ const narrowBucketLength = 60;
|
|||
*/
|
||||
export function resolveLookbackInterval(jobs: Job[], datafeeds: Datafeed[]): string {
|
||||
const bucketSpanInSeconds = Math.ceil(
|
||||
resolveMaxTimeInterval(jobs.map((v) => v.analysis_config.bucket_span)) ?? 0
|
||||
resolveMaxTimeInterval(jobs.map((v) => v.analysis_config.bucket_span!)) ?? 0
|
||||
);
|
||||
const queryDelayInSeconds = Math.ceil(
|
||||
resolveMaxTimeInterval(datafeeds.map((v) => v.query_delay).filter(isDefined)) ?? 0
|
||||
|
@ -45,7 +45,7 @@ export function getLookbackInterval(jobs: CombinedJobWithStats[]): string {
|
|||
}
|
||||
|
||||
export function getTopNBuckets(job: Job): number {
|
||||
const bucketSpan = parseInterval(job.analysis_config.bucket_span);
|
||||
const bucketSpan = parseInterval(job.analysis_config.bucket_span!);
|
||||
|
||||
if (bucketSpan === null) {
|
||||
throw new Error('Unable to resolve a bucket span length');
|
||||
|
|
|
@ -133,7 +133,7 @@ export function isSourceDataChartableForDetector(job: CombinedJob, detectorIndex
|
|||
const { detectors } = job.analysis_config;
|
||||
if (detectorIndex >= 0 && detectorIndex < detectors.length) {
|
||||
const dtr = detectors[detectorIndex];
|
||||
const functionName = dtr.function;
|
||||
const functionName = dtr.function as ML_JOB_AGGREGATION;
|
||||
|
||||
// Check that the function maps to an ES aggregation,
|
||||
// and that the partitioning field isn't mlcategory
|
||||
|
@ -334,7 +334,7 @@ export function isJobVersionGte(job: CombinedJob, version: string): boolean {
|
|||
// Note that the 'function' field in a record contains what the user entered e.g. 'high_count',
|
||||
// whereas the 'function_description' field holds an ML-built display hint for function e.g. 'count'.
|
||||
export function mlFunctionToESAggregation(
|
||||
functionName: ML_JOB_AGGREGATION | string
|
||||
functionName?: ML_JOB_AGGREGATION | string
|
||||
): ES_AGGREGATION | null {
|
||||
if (
|
||||
functionName === ML_JOB_AGGREGATION.MEAN ||
|
||||
|
|
|
@ -50,7 +50,7 @@ export const ConfigValidator: FC<ConfigValidatorProps> = React.memo(
|
|||
lookbackIntervalInSeconds &&
|
||||
alertIntervalInSeconds < lookbackIntervalInSeconds;
|
||||
|
||||
const bucketSpanDuration = parseInterval(jobConfigs[0].analysis_config.bucket_span);
|
||||
const bucketSpanDuration = parseInterval(jobConfigs[0].analysis_config.bucket_span!);
|
||||
const notificationDuration = bucketSpanDuration
|
||||
? Math.ceil(bucketSpanDuration.asMinutes()) *
|
||||
Math.min(
|
||||
|
|
|
@ -145,7 +145,7 @@ const MlAnomalyAlertTrigger: FC<MlAnomalyAlertTriggerProps> = ({
|
|||
const maxNumberOfBuckets = useMemo(() => {
|
||||
if (jobConfigs.length === 0) return;
|
||||
|
||||
const bucketDuration = parseInterval(jobConfigs[0].analysis_config.bucket_span);
|
||||
const bucketDuration = parseInterval(jobConfigs[0].analysis_config.bucket_span!);
|
||||
|
||||
const lookbackIntervalDuration = advancedSettings.lookbackInterval
|
||||
? parseInterval(advancedSettings.lookbackInterval)
|
||||
|
|
|
@ -389,7 +389,7 @@ function buildAppStateQueryParam(queryFieldNames: string[]) {
|
|||
// may contain dollar delimited partition / influencer entity tokens and
|
||||
// drilldown time range settings.
|
||||
async function getAnomalyDetectionJobTestUrl(job: Job, customUrl: MlUrlConfig): Promise<string> {
|
||||
const interval = parseInterval(job.analysis_config.bucket_span);
|
||||
const interval = parseInterval(job.analysis_config.bucket_span!);
|
||||
const bucketSpanSecs = interval !== null ? interval.asSeconds() : 0;
|
||||
|
||||
// By default, return configured url_value. Look to substitute any dollar-delimited
|
||||
|
@ -462,6 +462,7 @@ async function getAnomalyDetectionJobTestUrl(job: Job, customUrl: MlUrlConfig):
|
|||
undefined,
|
||||
jobConfig,
|
||||
datafeedConfig
|
||||
// @ts-expect-error TODO: fix after elasticsearch-js bump
|
||||
)) as unknown as estypes.MlPreviewDatafeedResponse<Record<string, unknown>>['data'];
|
||||
|
||||
const docTimeFieldName = job.data_description.time_field;
|
||||
|
|
|
@ -102,7 +102,7 @@ export const RevertModelSnapshotFlyout: FC<Props> = ({
|
|||
}, [calendarEvents]);
|
||||
|
||||
const createChartData = useCallback(async () => {
|
||||
const bucketSpanMs = parseInterval(job.analysis_config.bucket_span)!.asMilliseconds();
|
||||
const bucketSpanMs = parseInterval(job.analysis_config.bucket_span!)!.asMilliseconds();
|
||||
const eventRate = await loadEventRateForJob(job, bucketSpanMs, 100);
|
||||
const anomalyData = await loadAnomalyDataForJob(job, bucketSpanMs, 100);
|
||||
setEventRateData(eventRate);
|
||||
|
|
|
@ -141,7 +141,7 @@ export interface SourceIndicesWithGeoFields {
|
|||
// create new job objects based on standard job config objects
|
||||
export function createJobs(jobs: CombinedJob[]): ExplorerJob[] {
|
||||
return jobs.map((job) => {
|
||||
const bucketSpan = parseInterval(job.analysis_config.bucket_span);
|
||||
const bucketSpan = parseInterval(job.analysis_config.bucket_span!);
|
||||
return {
|
||||
id: job.job_id,
|
||||
selected: false,
|
||||
|
|
|
@ -237,7 +237,7 @@ export class CategorizationJobCreator extends JobCreator {
|
|||
? ML_JOB_AGGREGATION.COUNT
|
||||
: ML_JOB_AGGREGATION.RARE;
|
||||
|
||||
const bs = job.analysis_config.bucket_span;
|
||||
const bs = job.analysis_config.bucket_span!;
|
||||
this.setDetectorType(detectorType);
|
||||
if (dtr.partitionField !== null) {
|
||||
this.categorizationPerPartitionField = dtr.partitionField.id;
|
||||
|
|
|
@ -191,7 +191,7 @@ export class JobCreator {
|
|||
}
|
||||
|
||||
public get bucketSpan(): BucketSpan {
|
||||
return this._job_config.analysis_config.bucket_span;
|
||||
return this._job_config.analysis_config.bucket_span!;
|
||||
}
|
||||
|
||||
protected _setBucketSpanMs(bucketSpan: BucketSpan) {
|
||||
|
|
|
@ -59,7 +59,7 @@ export class SingleMetricJobCreator extends JobCreator {
|
|||
// overriding set means we need to override get too
|
||||
// JS doesn't do inheritance very well
|
||||
public get bucketSpan(): BucketSpan {
|
||||
return this._job_config.analysis_config.bucket_span;
|
||||
return this._job_config.analysis_config.bucket_span!;
|
||||
}
|
||||
|
||||
// aggregations need to be recreated whenever the detector or bucket_span change
|
||||
|
|
|
@ -75,7 +75,7 @@ export function getRichDetectors(
|
|||
}
|
||||
|
||||
return {
|
||||
agg: newJobCapsService.getAggById(d.function),
|
||||
agg: newJobCapsService.getAggById(d.function!),
|
||||
field,
|
||||
byField,
|
||||
overField,
|
||||
|
|
|
@ -335,7 +335,7 @@ export abstract class InferenceBase<TInferResponse> {
|
|||
}
|
||||
|
||||
private getDefaultInferenceConfig(): estypes.MlInferenceConfigUpdateContainer[keyof estypes.MlInferenceConfigUpdateContainer] {
|
||||
return this.model.inference_config[
|
||||
return this.model.inference_config![
|
||||
this.inferenceType as keyof estypes.MlInferenceConfigUpdateContainer
|
||||
];
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ export function processInferenceResult(
|
|||
inferenceResult: InferenceResult,
|
||||
model: estypes.MlTrainedModelConfig
|
||||
): FormattedTextClassificationResponse {
|
||||
const labels: string[] = model.inference_config.text_classification?.classification_labels ?? [];
|
||||
const labels: string[] = model.inference_config?.text_classification?.classification_labels ?? [];
|
||||
|
||||
let formattedResponse = [
|
||||
{
|
||||
|
|
|
@ -37,7 +37,7 @@ export const SelectedModel: FC<Props> = ({ model, inputType, deploymentId }) =>
|
|||
|
||||
const inferrer = useMemo<InferrerType | undefined>(() => {
|
||||
if (model.model_type === TRAINED_MODEL_TYPE.PYTORCH) {
|
||||
const taskType = Object.keys(model.inference_config)[0];
|
||||
const taskType = Object.keys(model.inference_config ?? {})[0];
|
||||
|
||||
switch (taskType) {
|
||||
case SUPPORTED_PYTORCH_TASKS.NER:
|
||||
|
|
|
@ -21,7 +21,7 @@ export function isTestable(modelItem: ModelItem, checkForState = false) {
|
|||
if (
|
||||
modelItem.model_type === TRAINED_MODEL_TYPE.PYTORCH &&
|
||||
PYTORCH_TYPES.includes(
|
||||
Object.keys(modelItem.inference_config)[0] as SupportedPytorchTasksType
|
||||
Object.keys(modelItem.inference_config ?? {})[0] as SupportedPytorchTasksType
|
||||
) &&
|
||||
(checkForState === false ||
|
||||
modelItem.stats?.deployment_stats?.some((v) => v.state === DEPLOYMENT_STATE.STARTED))
|
||||
|
|
|
@ -21,7 +21,7 @@ export function getViewableDetectors(selectedJob: CombinedJob): ViewableDetector
|
|||
viewableDetectors.push({
|
||||
index,
|
||||
detector_description: dtr.detector_description,
|
||||
function: dtr.function,
|
||||
function: dtr.function!,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
@ -24,7 +24,7 @@ export function getJobsObservable(
|
|||
switchMap((jobsIds) => anomalyDetectorService.getJobs$(jobsIds)),
|
||||
map((jobs) => {
|
||||
const explorerJobs: ExplorerJob[] = jobs.map((job) => {
|
||||
const bucketSpan = parseInterval(job.analysis_config.bucket_span);
|
||||
const bucketSpan = parseInterval(job.analysis_config.bucket_span!);
|
||||
return {
|
||||
id: job.job_id,
|
||||
selected: true,
|
||||
|
|
|
@ -481,7 +481,7 @@ export function alertingServiceProvider(
|
|||
}
|
||||
|
||||
const maxBucket = resolveMaxTimeInterval(
|
||||
jobsResponse.map((v) => v.analysis_config.bucket_span)
|
||||
jobsResponse.map((v) => v.analysis_config.bucket_span!)
|
||||
);
|
||||
|
||||
if (maxBucket === undefined) {
|
||||
|
@ -620,7 +620,7 @@ export function alertingServiceProvider(
|
|||
const datafeeds = await datafeedsService.getDatafeedByJobId(jobIds);
|
||||
|
||||
const maxBucketInSeconds = resolveMaxTimeInterval(
|
||||
jobsResponse.map((v) => v.analysis_config.bucket_span)
|
||||
jobsResponse.map((v) => v.analysis_config.bucket_span!)
|
||||
);
|
||||
|
||||
if (maxBucketInSeconds === undefined) {
|
||||
|
|
|
@ -29,6 +29,7 @@ export interface MlInferTrainedModelRequest extends estypes.MlInferTrainedModelR
|
|||
deployment_id?: string;
|
||||
}
|
||||
|
||||
// @ts-expect-error TODO: fix after elasticsearch-js bump
|
||||
export interface MlClient
|
||||
extends Omit<OrigMlClient, 'stopTrainedModelDeployment' | 'inferTrainedModel'> {
|
||||
anomalySearch: ReturnType<typeof searchProvider>['anomalySearch'];
|
||||
|
|
|
@ -80,7 +80,6 @@ export function jobsProvider(
|
|||
job_id: jobId,
|
||||
force: true,
|
||||
wait_for_completion: false,
|
||||
// @ts-expect-error delete_user_annotations is not in types yet
|
||||
delete_user_annotations: deleteUserAnnotations,
|
||||
});
|
||||
}
|
||||
|
@ -166,7 +165,6 @@ export function jobsProvider(
|
|||
const { task } = await mlClient.resetJob({
|
||||
job_id: jobId,
|
||||
wait_for_completion: false,
|
||||
// @ts-expect-error delete_user_annotations is not in types yet
|
||||
delete_user_annotations: deleteUserAnnotations,
|
||||
});
|
||||
results[jobId] = { reset: true, task };
|
||||
|
@ -254,7 +252,7 @@ export function jobsProvider(
|
|||
awaitingNodeAssignment: isJobAwaitingNodeAssignment(job),
|
||||
alertingRules: job.alerting_rules,
|
||||
jobTags: job.custom_settings?.job_tags ?? {},
|
||||
bucketSpanSeconds: parseInterval(job.analysis_config.bucket_span)!.asSeconds(),
|
||||
bucketSpanSeconds: parseInterval(job.analysis_config.bucket_span!)!.asSeconds(),
|
||||
};
|
||||
|
||||
if (jobIds.find((j) => j === tempJob.id)) {
|
||||
|
|
|
@ -162,9 +162,9 @@ function combineAllRollupFields(
|
|||
rollupFields[fieldName] = conf.fields[fieldName];
|
||||
} else {
|
||||
const aggs = conf.fields[fieldName];
|
||||
// @ts-expect-error fix type. our RollupFields type is better
|
||||
aggs.forEach((agg) => {
|
||||
if (rollupFields[fieldName].find((f) => f.agg === agg.agg) === null) {
|
||||
// @ts-expect-error TODO: fix after elasticsearch-js bump
|
||||
rollupFields[fieldName].push(agg);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -46,7 +46,6 @@ export async function validateDatafeedPreview(
|
|||
job_config: tempJob,
|
||||
datafeed_config: datafeed,
|
||||
},
|
||||
// @ts-expect-error es client types are wrong
|
||||
start,
|
||||
end,
|
||||
},
|
||||
|
|
|
@ -81,7 +81,7 @@ export async function validateTimeRange(
|
|||
}
|
||||
|
||||
// check for minimum time range (25 buckets or 2 hours, whichever is longer)
|
||||
const interval = parseInterval(job.analysis_config.bucket_span, true);
|
||||
const interval = parseInterval(job.analysis_config.bucket_span!, true);
|
||||
if (interval === null) {
|
||||
messages.push({ id: 'bucket_span_invalid' });
|
||||
} else {
|
||||
|
|
|
@ -734,7 +734,7 @@ export function anomalyChartsDataProvider(mlClient: MlClient, client: IScopedClu
|
|||
// Add extra properties used by the explorer dashboard charts.
|
||||
fullSeriesConfig.functionDescription = record.function_description;
|
||||
|
||||
const parsedBucketSpan = parseInterval(job.analysis_config.bucket_span);
|
||||
const parsedBucketSpan = parseInterval(job.analysis_config.bucket_span!);
|
||||
if (parsedBucketSpan !== null) {
|
||||
fullSeriesConfig.bucketSpanSeconds = parsedBucketSpan.asSeconds();
|
||||
}
|
||||
|
|
|
@ -139,7 +139,7 @@ export function managementRoutes({ router, routeGuard }: RouteInitialization) {
|
|||
state: modelStatsMapped[id].deployment_stats?.state ?? '',
|
||||
type: [
|
||||
m.model_type,
|
||||
...Object.keys(m.inference_config),
|
||||
...Object.keys(m.inference_config!),
|
||||
...(m.tags.includes(BUILT_IN_MODEL_TAG) ? [BUILT_IN_MODEL_TYPE] : []),
|
||||
],
|
||||
spaces: modelSpaces.trainedModels[id] ?? [],
|
||||
|
|
|
@ -52,7 +52,6 @@ export function registerAppRoutes({
|
|||
const { has_all_requested: hasAllPrivileges, cluster } =
|
||||
await clusterClient.asCurrentUser.security.hasPrivileges({
|
||||
body: {
|
||||
// @ts-expect-error @elastic/elasticsearch doesn't declare all possible values in SecurityClusterPrivilege
|
||||
cluster: [...APP_REQUIRED_CLUSTER_PRIVILEGES, ...APP_SLM_CLUSTER_PRIVILEGES],
|
||||
},
|
||||
});
|
||||
|
@ -71,7 +70,6 @@ export function registerAppRoutes({
|
|||
}
|
||||
|
||||
const indexHasAllPrivileges = APP_RESTORE_INDEX_PRIVILEGES.every((privilege) =>
|
||||
// @ts-expect-error SecurityClusterPrivilege doesn’t list all the possible privileges.
|
||||
privileges.includes(privilege)
|
||||
);
|
||||
|
||||
|
|
|
@ -47,7 +47,6 @@ export function registerPrivilegesRoute({ router, license }: RouteDependencies)
|
|||
const esClusterPrivilegesReq: Promise<SecurityHasPrivilegesResponse> =
|
||||
esClient.asCurrentUser.security.hasPrivileges({
|
||||
body: {
|
||||
// @ts-expect-error SecurityClusterPrivilege doesn’t contain all the priviledges
|
||||
cluster: APP_CLUSTER_PRIVILEGES,
|
||||
},
|
||||
});
|
||||
|
|
|
@ -59,7 +59,6 @@ export function registerTransformNodesRoutes({ router, license }: RouteDependenc
|
|||
const { has_all_requested: hasAllPrivileges } =
|
||||
await esClient.asCurrentUser.security.hasPrivileges({
|
||||
body: {
|
||||
// @ts-expect-error SecurityClusterPrivilege doesn’t contain all the priviledges
|
||||
cluster: NODES_INFO_PRIVILEGES,
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1515,10 +1515,10 @@
|
|||
"@elastic/transport" "^8.3.1"
|
||||
tslib "^2.4.0"
|
||||
|
||||
"@elastic/elasticsearch@npm:@elastic/elasticsearch-canary@8.6.0-canary.3":
|
||||
version "8.6.0-canary.3"
|
||||
resolved "https://registry.yarnpkg.com/@elastic/elasticsearch-canary/-/elasticsearch-canary-8.6.0-canary.3.tgz#dc518f5ae4bb502b08ff70e4d86fa7b859b3cbe3"
|
||||
integrity sha512-NvTZrRT/d5mJZ46pDlbgdKVIhA7ac504IGVaf7OV/7wHDmXjm8d/cG2UeQd8v37zMplGq2/853uFWGlXXjD0lQ==
|
||||
"@elastic/elasticsearch@npm:@elastic/elasticsearch-canary@8.8.0-canary.2":
|
||||
version "8.8.0-canary.2"
|
||||
resolved "https://registry.yarnpkg.com/@elastic/elasticsearch-canary/-/elasticsearch-canary-8.8.0-canary.2.tgz#10b5699541d644797b33c7e24058d2e55367d88d"
|
||||
integrity sha512-UxH8YUxcsqHXGh4t2PjuL0q03XunF9vCLHPAs9r+fQcaPXpNbEuv9jbNGXv/9TLyeAKYEgcq9Xm0p0Nk/Mh0lQ==
|
||||
dependencies:
|
||||
"@elastic/transport" "^8.3.1"
|
||||
tslib "^2.4.0"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue