Skip to content

Commit

Permalink
[ML] Versioning all ML APIs (#156949)
Browse files Browse the repository at this point in the history
Adds versioning to all of the ML kibana APIs.
Versions are added to the server side routes and to the client side
functions which call the routes.
Makes some small refactors to ensure only the functions inside
`ml/public/application/services/ml_api_service/` are where the ML APIs
are called.
Updates external plugins which call ML APIs to add the new versioning.
Updates API tests to add the API version to the request headers.

Our one public API has been given the version `'2023-05-15'`, all of the
internal APIs have been given the version `'1'`.

**Public APIs:**
`/api/ml/saved_objects/sync`

**Internal APIS:**
`/internal/ml/alerting/preview`
`/internal/ml/annotations`
`/internal/ml/annotations/index`
`/internal/ml/annotations/delete/{annotationId}`
`/internal/ml/anomaly_detectors`
`/internal/ml/anomaly_detectors/{jobId}`
`/internal/ml/anomaly_detectors/_stats`
`/internal/ml/anomaly_detectors/{jobId}/_stats`
`/internal/ml/anomaly_detectors/{jobId}`
`/internal/ml/anomaly_detectors/{jobId}/_update`
`/internal/ml/anomaly_detectors/{jobId}/_open`
`/internal/ml/anomaly_detectors/{jobId}/_close`
`/internal/ml/anomaly_detectors/{jobId}/_reset`
`/internal/ml/anomaly_detectors/{jobId}`
`/internal/ml/anomaly_detectors/_validate/detector`
`/internal/ml/anomaly_detectors/{jobId}/_forecast`
`/internal/ml/anomaly_detectors/{jobId}/results/records`
`/internal/ml/anomaly_detectors/{jobId}/results/buckets/{timestamp?}`
`/internal/ml/anomaly_detectors/{jobId}/results/overall_buckets`
`/internal/ml/anomaly_detectors/{jobId}/results/categories/{categoryId}`
`/internal/ml/anomaly_detectors/{jobId}/model_snapshots`
`/internal/ml/anomaly_detectors/{jobId}/model_snapshots/{snapshotId}`

`/internal/ml/anomaly_detectors/{jobId}/model_snapshots/{snapshotId}/_update`
`/internal/ml/anomaly_detectors/{jobId}/model_snapshots/{snapshotId}`
`/internal/ml/calendars`
`/internal/ml/calendars/{calendarIds}`
`/internal/ml/calendars`
`/internal/ml/calendars/{calendarId}`
`/internal/ml/calendars/{calendarId}`
`/internal/ml/data_frame/analytics`
`/internal/ml/data_frame/analytics/{analyticsId}`
`/internal/ml/data_frame/analytics/_stats`
`/internal/ml/data_frame/analytics/{analyticsId}/_stats`
`/internal/ml/data_frame/analytics/{analyticsId}`
`/internal/ml/data_frame/_evaluate`
`/internal/ml/data_frame/analytics/_explain`
`/internal/ml/data_frame/analytics/{analyticsId}`
`/internal/ml/data_frame/analytics/{analyticsId}/_start`
`/internal/ml/data_frame/analytics/{analyticsId}/_stop`
`/internal/ml/data_frame/analytics/{analyticsId}/_update`
`/internal/ml/data_frame/analytics/{analyticsId}/messages`
`/internal/ml/data_frame/analytics/jobs_exist`
`/internal/ml/data_frame/analytics/map/{analyticsId}`
`/internal/ml/data_frame/analytics/new_job_caps/{indexPattern}`
`/internal/ml/data_frame/analytics/validate`
`/internal/ml/data_visualizer/get_field_histograms/{indexPattern}`
`/internal/ml/datafeeds`
`/internal/ml/datafeeds/{datafeedId}`
`/internal/ml/datafeeds/_stats`
`/internal/ml/datafeeds/{datafeedId}/_stats`
`/internal/ml/datafeeds/{datafeedId}`
`/internal/ml/datafeeds/{datafeedId}/_update`
`/internal/ml/datafeeds/{datafeedId}`
`/internal/ml/datafeeds/{datafeedId}/_start`
`/internal/ml/datafeeds/{datafeedId}/_stop`
`/internal/ml/datafeeds/{datafeedId}/_preview`
`/internal/ml/fields_service/field_cardinality`
`/internal/ml/fields_service/time_field_range`
`/internal/ml/filters`
`/internal/ml/filters/{filterId}`
`/internal/ml/filters`
`/internal/ml/filters/{filterId}`
`/internal/ml/filters/{filterId}`
`/internal/ml/filters/_stats`
`/internal/ml/indices/field_caps`
`/internal/ml/job_audit_messages/messages/{jobId}`
`/internal/ml/job_audit_messages/messages`
`/internal/ml/job_audit_messages/clear_messages`
`/internal/ml/jobs/force_start_datafeeds`
`/internal/ml/jobs/stop_datafeeds`
`/internal/ml/jobs/delete_jobs`
`/internal/ml/jobs/close_jobs`
`/internal/ml/jobs/reset_jobs`
`/internal/ml/jobs/force_stop_and_close_job`
`/internal/ml/jobs/jobs_summary`
`/internal/ml/jobs/jobs_with_geo`
`/internal/ml/jobs/jobs_with_time_range`
`/internal/ml/jobs/job_for_cloning`
`/internal/ml/jobs/jobs`
`/internal/ml/jobs/groups`
`/internal/ml/jobs/update_groups`
`/internal/ml/jobs/blocking_jobs_tasks`
`/internal/ml/jobs/jobs_exist`
`/internal/ml/jobs/new_job_caps/{indexPattern}`
`/internal/ml/jobs/new_job_line_chart`
`/internal/ml/jobs/new_job_population_chart`
`/internal/ml/jobs/all_jobs_and_group_ids`
`/internal/ml/jobs/look_back_progress`
`/internal/ml/jobs/categorization_field_examples`
`/internal/ml/jobs/top_categories`
`/internal/ml/jobs/datafeed_preview`
`/internal/ml/jobs/revert_model_snapshot`
`/internal/ml/jobs/bulk_create`
`/internal/ml/validate/estimate_bucket_span`
`/internal/ml/validate/calculate_model_memory_limit`
`/internal/ml/validate/cardinality`
`/internal/ml/validate/job`
`/internal/ml/validate/datafeed_preview`
`/internal/ml/json_schema`
`/internal/ml/management/list/{listType}`
`/internal/ml/model_management/nodes_overview`
`/internal/ml/model_management/memory_usage`
`/internal/ml/modules/recognize/{indexPatternTitle}`
`/internal/ml/modules/get_module/{moduleId?}`
`/internal/ml/modules/setup/{moduleId}`
`/internal/ml/modules/jobs_exist/{moduleId}`
`/internal/ml/notifications`
`/internal/ml/notifications/count`
`/internal/ml/results/anomalies_table_data`
`/internal/ml/results/category_definition`
`/internal/ml/results/max_anomaly_score`
`/internal/ml/results/category_examples`
`/internal/ml/results/partition_fields_values`
`/internal/ml/results/anomaly_search`
`/internal/ml/results/{jobId}/categorizer_stats`
`/internal/ml/results/category_stopped_partitions`
`/internal/ml/results/datafeed_results_chart`
`/internal/ml/results/anomaly_charts`
`/internal/ml/results/anomaly_records`
`/internal/ml/saved_objects/status`
`/internal/ml/saved_objects/initialize`
`/internal/ml/saved_objects/sync_check`
`/internal/ml/saved_objects/update_jobs_spaces`
`/internal/ml/saved_objects/update_trained_models_spaces`
`/internal/ml/saved_objects/remove_item_from_current_space`
`/internal/ml/saved_objects/jobs_spaces`
`/internal/ml/saved_objects/trained_models_spaces`
`/internal/ml/saved_objects/can_delete_ml_space_aware_item/{jobType}`
`/internal/ml/_has_privileges`
`/internal/ml/ml_capabilities`
`/internal/ml/ml_node_count`
`/internal/ml/info`
`/internal/ml/es_search`
`/internal/ml/index_exists`
`/internal/ml/trained_models/{modelId?}`
`/internal/ml/trained_models/_stats`
`/internal/ml/trained_models/{modelId}/_stats`
`/internal/ml/trained_models/{modelId}/pipelines`
`/internal/ml/trained_models/{modelId}`
`/internal/ml/trained_models/{modelId}`
`/internal/ml/trained_models/{modelId}/deployment/_start`

`/internal/ml/trained_models/{modelId}/{deploymentId}/deployment/_update`
`/internal/ml/trained_models/{modelId}/{deploymentId}/deployment/_stop`
`/internal/ml/trained_models/pipeline_simulate`
`/internal/ml/trained_models/infer/{modelId}/{deploymentId}`
  • Loading branch information
jgowdyelastic authored May 23, 2023
1 parent 46dc7ff commit 203d306
Show file tree
Hide file tree
Showing 190 changed files with 5,817 additions and 4,637 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import type { GetTimeFieldRangeResponse } from './types';
*/
export type SetFullTimeRangeApiPath =
| '/internal/file_upload/time_field_range'
| '/api/ml/fields_service/time_field_range';
| '/internal/ml/fields_service/time_field_range';

/**
* Determines the full available time range of the given Data View and updates
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ describe('MLModelsApiLogic', () => {
it('calls the ml api', async () => {
http.get.mockResolvedValue(mlModelStats);
const result = await getMLModelsStats();
expect(http.get).toHaveBeenCalledWith('/api/ml/trained_models/_stats');
expect(http.get).toHaveBeenCalledWith('/internal/ml/trained_models/_stats', { version: '1' });
expect(result).toEqual(mlModelStats);
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ export interface GetMlModelsStatsResponse {
}

export const getMLModelsStats = async () => {
return await HttpLogic.values.http.get<GetMlModelsStatsResponse>('/api/ml/trained_models/_stats');
return await HttpLogic.values.http.get<GetMlModelsStatsResponse>(
'/internal/ml/trained_models/_stats',
{ version: '1' }
);
};

export const MLModelsStatsApiLogic = createApiLogic(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ describe('MLModelsApiLogic', () => {
it('calls the ml api', async () => {
http.get.mockResolvedValue(mlModels);
const result = await getMLModels();
expect(http.get).toHaveBeenCalledWith('/api/ml/trained_models', {
expect(http.get).toHaveBeenCalledWith('/internal/ml/trained_models', {
query: { size: 1000, with_pipelines: true },
version: '1',
});
expect(result).toEqual(mlModels);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,13 @@ export type GetMlModelsArgs = number | undefined;
export type GetMlModelsResponse = TrainedModelConfigResponse[];

export const getMLModels = async (size: GetMlModelsArgs = 1000) => {
return await HttpLogic.values.http.get<TrainedModelConfigResponse[]>('/api/ml/trained_models', {
query: { size, with_pipelines: true },
});
return await HttpLogic.values.http.get<TrainedModelConfigResponse[]>(
'/internal/ml/trained_models',
{
query: { size, with_pipelines: true },
version: '1',
}
);
};

export const MLModelsApiLogic = createApiLogic(['ml_models_api_logic'], getMLModels, {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@ export const callDeleteJobs = async <JobType extends string>(
const { spaceId, logViewId, jobTypes } = requestArgs;

// NOTE: Deleting the jobs via this API will delete the datafeeds at the same time
const deleteJobsResponse = await fetch('/api/ml/jobs/delete_jobs', {
const deleteJobsResponse = await fetch('/internal/ml/jobs/delete_jobs', {
method: 'POST',
version: '1',
body: JSON.stringify(
deleteJobsRequestPayloadRT.encode({
jobIds: jobTypes.map((jobType) => getJobId(spaceId, logViewId, jobType)),
Expand All @@ -37,7 +38,9 @@ export const callDeleteJobs = async <JobType extends string>(
};

export const callGetJobDeletionTasks = async (fetch: HttpHandler) => {
const jobDeletionTasksResponse = await fetch('/api/ml/jobs/deleting_jobs_tasks');
const jobDeletionTasksResponse = await fetch('/internal/ml/jobs/deleting_jobs_tasks', {
version: '1',
});

return decodeOrThrow(getJobDeletionTasksResponsePayloadRT)(jobDeletionTasksResponse);
};
Expand All @@ -55,8 +58,9 @@ export const callStopDatafeeds = async <JobType extends string>(
const { spaceId, logViewId, jobTypes } = requestArgs;

// Stop datafeed due to https://github.com/elastic/kibana/issues/44652
const stopDatafeedResponse = await fetch('/api/ml/jobs/stop_datafeeds', {
const stopDatafeedResponse = await fetch('/internal/ml/jobs/stop_datafeeds', {
method: 'POST',
version: '1',
body: JSON.stringify(
stopDatafeedsRequestPayloadRT.encode({
datafeedIds: jobTypes.map((jobType) => getDatafeedId(spaceId, logViewId, jobType)),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ export const callJobsSummaryAPI = async <JobType extends string>(
fetch: HttpHandler
) => {
const { spaceId, logViewId, jobTypes } = requestArgs;
const response = await fetch('/api/ml/jobs/jobs_summary', {
const response = await fetch('/internal/ml/jobs/jobs_summary', {
method: 'POST',
version: '1',
body: JSON.stringify(
fetchJobStatusRequestPayloadRT.encode({
jobIds: jobTypes.map((jobType) => getJobId(spaceId, logViewId, jobType)),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ import { jobCustomSettingsRT } from '../../../../../common/log_analysis';
import { decodeOrThrow } from '../../../../../common/runtime_types';

export const callGetMlModuleAPI = async (moduleId: string, fetch: HttpHandler) => {
const response = await fetch(`/api/ml/modules/get_module/${moduleId}`, {
const response = await fetch(`/internal/ml/modules/get_module/${moduleId}`, {
method: 'GET',
version: '1',
});

return decodeOrThrow(getMlModuleResponsePayloadRT)(response);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,9 @@ export const callSetupMlModuleAPI = async (requestArgs: RequestArgs, fetch: Http
useDedicatedIndex = false,
} = requestArgs;

const response = await fetch(`/api/ml/modules/setup/${moduleId}`, {
const response = await fetch(`/internal/ml/modules/setup/${moduleId}`, {
method: 'POST',
version: '1',
body: JSON.stringify(
setupMlModuleRequestPayloadRT.encode({
start,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ export const useLogAnalysisCapabilities = () => {
{
cancelPreviousOn: 'resolution',
createPromise: async () => {
const rawResponse = await services.http.fetch('/api/ml/ml_capabilities');
const rawResponse = await services.http.fetch('/internal/ml/ml_capabilities', {
version: '1',
});

return decodeOrThrow(getMlCapabilitiesResponsePayloadRT)(rawResponse);
},
Expand Down
10 changes: 7 additions & 3 deletions x-pack/plugins/infra/public/containers/ml/api/ml_cleanup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@ export const callDeleteJobs = async <JobType extends string>(
const { spaceId, sourceId, jobTypes } = requestArgs;

// NOTE: Deleting the jobs via this API will delete the datafeeds at the same time
const deleteJobsResponse = await fetch('/api/ml/jobs/delete_jobs', {
const deleteJobsResponse = await fetch('/internal/ml/jobs/delete_jobs', {
method: 'POST',
version: '1',
body: JSON.stringify(
deleteJobsRequestPayloadRT.encode({
jobIds: jobTypes.map((jobType) => getJobId(spaceId, sourceId, jobType)),
Expand All @@ -36,7 +37,9 @@ export const callDeleteJobs = async <JobType extends string>(
};

export const callGetJobDeletionTasks = async (fetch: HttpHandler) => {
const jobDeletionTasksResponse = await fetch('/api/ml/jobs/deleting_jobs_tasks');
const jobDeletionTasksResponse = await fetch('/internal/ml/jobs/deleting_jobs_tasks', {
version: '1',
});

return decodeOrThrow(getJobDeletionTasksResponsePayloadRT)(jobDeletionTasksResponse);
};
Expand All @@ -54,8 +57,9 @@ export const callStopDatafeeds = async <JobType extends string>(
const { spaceId, sourceId, jobTypes } = requestArgs;

// Stop datafeed due to https://github.com/elastic/kibana/issues/44652
const stopDatafeedResponse = await fetch('/api/ml/jobs/stop_datafeeds', {
const stopDatafeedResponse = await fetch('/internal/ml/jobs/stop_datafeeds', {
method: 'POST',
version: '1',
body: JSON.stringify(
stopDatafeedsRequestPayloadRT.encode({
datafeedIds: jobTypes.map((jobType) => getDatafeedId(spaceId, sourceId, jobType)),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ export const callJobsSummaryAPI = async <JobType extends string>(
fetch: HttpHandler
) => {
const { spaceId, sourceId, jobTypes } = requestArgs;
const response = await fetch('/api/ml/jobs/jobs_summary', {
const response = await fetch('/internal/ml/jobs/jobs_summary', {
method: 'POST',
version: '1',
body: JSON.stringify(
fetchJobStatusRequestPayloadRT.encode({
jobIds: jobTypes.map((jobType) => getJobId(spaceId, sourceId, jobType)),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ import { jobCustomSettingsRT } from '../../../../common/log_analysis';
import { decodeOrThrow } from '../../../../common/runtime_types';

export const callGetMlModuleAPI = async (moduleId: string, fetch: HttpHandler) => {
const response = await fetch(`/api/ml/modules/get_module/${moduleId}`, {
const response = await fetch(`/internal/ml/modules/get_module/${moduleId}`, {
method: 'GET',
version: '1',
});

return decodeOrThrow(getMlModuleResponsePayloadRT)(response);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,9 @@ export const callSetupMlModuleAPI = async (requestArgs: RequestArgs, fetch: Http
query,
} = requestArgs;

const response = await fetch(`/api/ml/modules/setup/${moduleId}`, {
const response = await fetch(`/internal/ml/modules/setup/${moduleId}`, {
method: 'POST',
version: '1',
body: JSON.stringify(
setupMlModuleRequestPayloadRT.encode({
start,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ export const useInfraMLCapabilities = () => {
{
cancelPreviousOn: 'resolution',
createPromise: async () => {
const rawResponse = await services.http.fetch('/api/ml/ml_capabilities');
const rawResponse = await services.http.fetch('/internal/ml/ml_capabilities', {
version: '1',
});

return pipe(
getMlCapabilitiesResponsePayloadRT.decode(rawResponse),
Expand Down
3 changes: 2 additions & 1 deletion x-pack/plugins/ml/common/constants/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ export const PLUGIN_ICON_SOLUTION = 'logoKibana';
export const ML_APP_NAME = i18n.translate('xpack.ml.navMenu.mlAppNameText', {
defaultMessage: 'Machine Learning',
});
export const ML_BASE_PATH = '/api/ml';
export const ML_INTERNAL_BASE_PATH = '/internal/ml';
export const ML_EXTERNAL_BASE_PATH = '/api/ml';
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export interface MlSummaryJob {

/**
* Used in older implementations of the job config, where the datafeed was placed inside the job for convenience.
* This will be populated if the job's id has been passed to the /api/ml/jobs/jobs_summary endpoint.
* This will be populated if the job's id has been passed to the /internal/ml/jobs/jobs_summary endpoint.
*/
fullJob?: CombinedJob;

Expand Down
8 changes: 8 additions & 0 deletions x-pack/plugins/ml/common/types/modules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,14 @@ export interface Module {
kibana: KibanaObjects;
}

export interface RecognizeResult {
id: string;
title: string;
query: any;
description: string;
logo: Logo;
}

export interface FileBasedModule extends Omit<Module, 'jobs' | 'datafeeds' | 'kibana'> {
jobs: Array<{ file: string; id: string }>;
datafeeds: Array<{ file: string; job_id: string; id: string }>;
Expand Down
17 changes: 17 additions & 0 deletions x-pack/plugins/ml/common/util/job_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -916,3 +916,20 @@ export function isKnownEmptyQuery(query: QueryDslQueryContainer) {

return false;
}

/**
* Extract unique influencers from the job or collection of jobs
* @param jobs
*/
export function extractInfluencers(jobs: Job | Job[]): string[] {
if (!Array.isArray(jobs)) {
jobs = [jobs];
}
const influencers = new Set<string>();
for (const job of jobs) {
for (const influencer of job.analysis_config.influencers || []) {
influencers.add(influencer);
}
}
return Array.from(influencers);
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,26 +23,18 @@ import { mlNodesAvailable, getMlNodeCount } from '../../ml_nodes_check/check_ml_
import { checkPermission } from '../../capabilities/check_capabilities';
import { MlPageHeader } from '../../components/page_header';

interface RecognizerModule {
id: string;
title: string;
query: Record<string, object>;
description: string;
logo: {
icon: string;
};
}

export const IndexDataVisualizerPage: FC = () => {
useTimefilter({ timeRangeSelector: false, autoRefreshSelector: false });
const {
services: {
http,
docLinks,
dataVisualizer,
data: {
dataViews: { get: getDataView },
},
mlServices: {
mlApiServices: { recognizeIndex },
},
},
} = useMlKibana();
const mlLocator = useMlLocator()!;
Expand Down Expand Up @@ -140,18 +132,14 @@ export const IndexDataVisualizerPage: FC = () => {
const getAsyncRecognizedModuleCards = async (params: GetAdditionalLinksParams) => {
const { dataViewId, dataViewTitle } = params;
try {
const modules = await http.fetch<RecognizerModule[]>(
`/api/ml/modules/recognize/${dataViewTitle}`,
{
method: 'GET',
}
);
const modules = await recognizeIndex({ indexPatternTitle: dataViewTitle! });

return modules?.map(
(m): ResultLink => ({
id: m.id,
title: m.title,
description: m.description,
icon: m.logo.icon,
icon: m.logo?.icon ?? '',
type: 'index',
getUrl: async () => {
return await mlLocator.getUrl({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import moment from 'moment';
import { FullTimeRangeSelector, FROZEN_TIER_PREFERENCE } from '@kbn/ml-date-picker';
import { useTimefilter, type GetTimeFieldRangeResponse } from '@kbn/ml-date-picker';
import { useStorage } from '@kbn/ml-local-storage';
import { ML_INTERNAL_BASE_PATH } from '../../../../../../../common/constants/app';
import { WizardNav } from '../wizard_nav';
import { StepProps, WIZARD_STEPS } from '../step_types';
import { JobCreatorContext } from '../job_creator_context';
Expand Down Expand Up @@ -135,7 +136,7 @@ export const TimeRangeStep: FC<StepProps> = ({ setCurrentStep, isCurrentStep })
disabled={false}
callback={fullTimeRangeCallback}
timefilter={timefilter}
apiPath="/api/ml/fields_service/time_field_range"
apiPath={`${ML_INTERNAL_BASE_PATH}/fields_service/time_field_range`}
/>
</EuiFlexItem>
<EuiFlexItem />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,53 +7,32 @@

import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { Job, JobId } from '../../../common/types/anomaly_detection_jobs';
import { basePath } from './ml_api_service';
import type { Job, JobId } from '../../../common/types/anomaly_detection_jobs';
import { HttpService } from './http_service';
import { type MlApiServices, mlApiServicesProvider } from './ml_api_service';

export class AnomalyDetectorService {
private readonly apiBasePath = basePath() + '/anomaly_detectors';
private mlApiServices: MlApiServices;

constructor(private httpService: HttpService) {}
constructor(httpService: HttpService) {
this.mlApiServices = mlApiServicesProvider(httpService);
}

/**
* Fetches a single job object
* @param jobId
*/
getJobById$(jobId: JobId): Observable<Job> {
return this.httpService
.http$<{ count: number; jobs: Job[] }>({
path: `${this.apiBasePath}/${jobId}`,
})
.pipe(map((response) => response.jobs[0]));
return this.getJobs$([jobId]).pipe(map((jobs) => jobs[0]));
}

/**
* Fetches anomaly detection jobs by ids
* @param jobIds
*/
getJobs$(jobIds: JobId[]): Observable<Job[]> {
return this.httpService
.http$<{ count: number; jobs: Job[] }>({
path: `${this.apiBasePath}/${jobIds.join(',')}`,
})
return this.mlApiServices
.getJobs$({ jobId: jobIds.join(',') })
.pipe(map((response) => response.jobs));
}

/**
* Extract unique influencers from the job or collection of jobs
* @param jobs
*/
extractInfluencers(jobs: Job | Job[]): string[] {
if (!Array.isArray(jobs)) {
jobs = [jobs];
}
const influencers = new Set<string>();
for (const job of jobs) {
for (const influencer of job.analysis_config.influencers || []) {
influencers.add(influencer);
}
}
return Array.from(influencers);
}
}
Loading

0 comments on commit 203d306

Please sign in to comment.