diff --git a/src/index.ts b/src/index.ts index e42ce48..0858c4d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,6 +4,7 @@ import ProjectsModule from './projects/projectsModule'; import SQLModule from './sql/sqlModule'; import ViewsModule from './views/viewsModule'; import Constants from './constants'; +import JobsModule from './jobs/jobsModule'; import { createDefaultAxiosInstance, isLocalEndpoint, @@ -28,6 +29,7 @@ import { FinetuneOptions, TrainingOptions } from './models/trainingOptions'; import Project from './projects/project'; import SqlQueryResult from './sql/sqlQueryResult'; import Table from './tables/table'; +import Job from './jobs/job'; import { JsonPrimitive, JsonValue } from './util/json'; import View from './views/view'; import MLEnginesModule from './ml_engines/ml_enginesModule'; @@ -60,6 +62,12 @@ const Callbacks = new CallbacksModule.CallbacksRestApiClient( defaultAxiosInstance, httpAuthenticator ); +const Jobs = new JobsModule.JobsRestApiClient( + SQL, + defaultAxiosInstance, + httpAuthenticator +); + const getAxiosInstance = function (options: ConnectionOptions): Axios { const httpClient = options.httpClient || defaultAxiosInstance; @@ -82,6 +90,7 @@ const getAxiosInstance = function (options: ConnectionOptions): Axios { const connect = async function (options: ConnectionOptions): Promise { const httpClient = getAxiosInstance(options); SQL.client = httpClient; + Jobs.client = httpClient; Projects.client = httpClient; MLEngines.client = httpClient; Callbacks.client = httpClient; @@ -114,6 +123,7 @@ export default { Databases, Models, Projects, + Jobs, Tables, Views, MLEngines, @@ -133,6 +143,7 @@ export { Project, SqlQueryResult, Table, + Job, JsonPrimitive, JsonValue, View, diff --git a/src/jobs/JobsApiClient.ts b/src/jobs/JobsApiClient.ts new file mode 100644 index 0000000..8cf3c40 --- /dev/null +++ b/src/jobs/JobsApiClient.ts @@ -0,0 +1,7 @@ +import Job from './job'; + +/** Abstract class outlining Jobs operations supported by the SDK. */ +export default abstract class JobsApiClient { + + abstract list(name?: string, project?: string): Promise>; +} \ No newline at end of file diff --git a/src/jobs/job.ts b/src/jobs/job.ts new file mode 100644 index 0000000..bccb0c1 --- /dev/null +++ b/src/jobs/job.ts @@ -0,0 +1,25 @@ +export default class Job { + + name: string; + query: string; + if_query: string; + start_at: string; + end_at: string; + schedule_str: string; + + constructor( + name: string, + query: string, + if_query: string, + start_at: string, + end_at: string, + schedule_str: string + ) { + this.name = name; + this.query = query; + this.if_query = if_query; + this.start_at = start_at; + this.end_at = end_at; + this.schedule_str = schedule_str; + } +} \ No newline at end of file diff --git a/src/jobs/jobsModule.ts b/src/jobs/jobsModule.ts new file mode 100644 index 0000000..db69c5a --- /dev/null +++ b/src/jobs/jobsModule.ts @@ -0,0 +1,3 @@ +import JobsRestApiClient from './jobsRestApiClient'; + +export default { JobsRestApiClient }; \ No newline at end of file diff --git a/src/jobs/jobsRestApiClient.ts b/src/jobs/jobsRestApiClient.ts new file mode 100644 index 0000000..11768c5 --- /dev/null +++ b/src/jobs/jobsRestApiClient.ts @@ -0,0 +1,80 @@ +import { Axios } from 'axios'; +import JobsApiClient from './jobsApiClient'; +import mysql from 'mysql'; +import Job from './job'; +import SqlApiClient from '../sql/sqlApiClient'; +import HttpAuthenticator from '../httpAuthenticator'; +import { MindsDbError } from '../errors'; + +/** Implementation of JobsApiClient that goes through the REST API. */ +export default class JobsRestApiClient extends JobsApiClient { + /** Axios client to send all HTTP requests. */ + client: Axios; + + /** Authenticator to use for reauthenticating if needed. */ + authenticator: HttpAuthenticator; + + /** SQL API client to send all SQL query requests. */ + sqlClient: SqlApiClient; + + /** + * Constructor for Jobs API client. + * @param {Axios} client - Axios instance to send all HTTP requests. + */ + constructor(client: Axios, authenticator: HttpAuthenticator, sqlClient: SqlApiClient) { + super(); + this.client = client; + this.authenticator = authenticator; + this.sqlClient = sqlClient; + } + + + /** + * Retrieves a list of jobs from the information schema. + * + * This asynchronous method queries the database for jobs, optionally filtering + * by project name and/or job name. If both parameters are provided, the method + * combines the filters to return a more specific list of jobs. + * + * @param {string} [name] - The optional name of the job to filter the results. + * If provided, only jobs matching this name will be included. + * @param {string} [project] - The optional project name to filter the results. + * If provided, only jobs associated with this project will be included. + * + * @returns {Promise>} - A promise that resolves to an array of Job objects + * representing the jobs retrieved from the database. + * + * @throws {MindsDbError} - Throws an error if the SQL query execution fails, + * containing the error message returned from the database. + * + * @example + * const jobs = await list('myJob', 'myProject'); + * console.log(jobs); + */ + override async list(name?: string, project?: string): Promise> { + const selectClause = `SELECT * FROM information_schema.jobs`; + let projectClause = ''; + let nameClause = ''; + if(project){ + projectClause = `WHERE project = ${mysql.escape(project)}`; + } + + if(name){ + nameClause = `name = ${mysql.escape(name)}`; + } + + const listJobsQuery = [selectClause, projectClause, nameClause].join( + '\n' + ); + + const sqlQueryResult = await this.sqlClient.runQuery(listJobsQuery); + if (sqlQueryResult.error_message) { + throw new MindsDbError(sqlQueryResult.error_message); + } + return sqlQueryResult.rows.map( + (r) => new Job(r['NAME'], r['QUERY'], r['IF_QUERY'], r['START_AT'], r['END_AT'], r['SCHEDULE_STR']) + ); + + } + +} \ No newline at end of file