[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:
Yan Savitski 2023-05-30 14:16:40 +02:00 committed by GitHub
parent 76e36beead
commit 815fddb9f0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
46 changed files with 129 additions and 169 deletions

View file

@ -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",

View file

@ -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,

View file

@ -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: [],

View file

@ -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.

View file

@ -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: {

View file

@ -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,

View file

@ -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: {

View file

@ -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);

View file

@ -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: {

View file

@ -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) => {

View file

@ -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);

View file

@ -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',

View file

@ -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({

View file

@ -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`
);

View file

@ -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'
)

View file

@ -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 } })
),
});

View file

@ -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 doesnt contain all the priviledges
body: { cluster: APP_CLUSTER_REQUIRED_PRIVILEGES },
});

View file

@ -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');

View file

@ -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 ||

View file

@ -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(

View file

@ -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)

View file

@ -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;

View file

@ -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);

View file

@ -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,

View file

@ -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;

View file

@ -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) {

View file

@ -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

View file

@ -75,7 +75,7 @@ export function getRichDetectors(
}
return {
agg: newJobCapsService.getAggById(d.function),
agg: newJobCapsService.getAggById(d.function!),
field,
byField,
overField,

View file

@ -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
];
}

View file

@ -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 = [
{

View file

@ -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:

View file

@ -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))

View file

@ -21,7 +21,7 @@ export function getViewableDetectors(selectedJob: CombinedJob): ViewableDetector
viewableDetectors.push({
index,
detector_description: dtr.detector_description,
function: dtr.function,
function: dtr.function!,
});
}
});

View file

@ -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,

View file

@ -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) {

View file

@ -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'];

View file

@ -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)) {

View file

@ -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);
}
});

View file

@ -46,7 +46,6 @@ export async function validateDatafeedPreview(
job_config: tempJob,
datafeed_config: datafeed,
},
// @ts-expect-error es client types are wrong
start,
end,
},

View file

@ -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 {

View file

@ -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();
}

View file

@ -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] ?? [],

View file

@ -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 doesnt list all the possible privileges.
privileges.includes(privilege)
);

View file

@ -47,7 +47,6 @@ export function registerPrivilegesRoute({ router, license }: RouteDependencies)
const esClusterPrivilegesReq: Promise<SecurityHasPrivilegesResponse> =
esClient.asCurrentUser.security.hasPrivileges({
body: {
// @ts-expect-error SecurityClusterPrivilege doesnt contain all the priviledges
cluster: APP_CLUSTER_PRIVILEGES,
},
});

View file

@ -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 doesnt contain all the priviledges
cluster: NODES_INFO_PRIVILEGES,
},
});

View file

@ -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"