diff --git a/src/index.ts b/src/index.ts index e42ce48..29a5f32 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,6 +3,7 @@ import DatabasesModule from './databases/databasesModule'; import ProjectsModule from './projects/projectsModule'; import SQLModule from './sql/sqlModule'; import ViewsModule from './views/viewsModule'; +import JobsModule from './jobs/jobsModule'; import Constants from './constants'; import { createDefaultAxiosInstance, @@ -51,6 +52,7 @@ const Projects = new ProjectsModule.ProjectsRestApiClient( ); const Tables = new TablesModule.TablesRestApiClient(SQL); const Views = new ViewsModule.ViewsRestApiClient(SQL); +const Jobs = new JobsModule.JobsRestApiClient(SQL); const MLEngines = new MLEnginesModule.MLEnginesRestApiClient( SQL, defaultAxiosInstance, @@ -116,6 +118,7 @@ export default { Projects, Tables, Views, + Jobs, MLEngines, Callbacks, }; diff --git a/src/jobs/job.ts b/src/jobs/job.ts new file mode 100644 index 0000000..6c54f35 --- /dev/null +++ b/src/jobs/job.ts @@ -0,0 +1,139 @@ +import JobsApiClient from './jobsApiClient'; +import { MindsDbError } from '../errors'; + +/** + * Represents a MindsDB job and provides methods to build and create it. + */ +export default class Job { + /** API client to use for executing job operations. */ + private jobsApiClient: JobsApiClient; + + /** Name of the job. */ + name: string; + + /** Project the job belongs to. */ + project: string; + + /** Accumulated queries to be executed by the job. */ + private queries: string[]; + + /** Optional start date for the job. */ + private start?: string; + + /** Optional end date for the job. */ + private end?: string; + + /** Optional repetition frequency. */ + private every?: string; + + /** Optional condition for job execution. */ + private ifCondition?: string; + + /** + * @param {JobsApiClient} jobsApiClient - API client to use for executing job operations. + * @param {string} name - Name of the job. + * @param {string} project - Project the job belongs to. + */ + constructor(jobsApiClient: JobsApiClient, name: string, project: string) { + this.jobsApiClient = jobsApiClient; + this.name = name; + this.project = project; + this.queries = []; + } + + /** + * Adds a query to the job. + * @param {string} query - The query to add. + * @returns {Job} - The current job instance (for chaining). + * @throws {MindsDbError} - If the query is invalid. + */ + addQuery(query: string): Job { + if (typeof query === 'string') { + this.queries.push(query); + } else { + throw new MindsDbError('Invalid query type. Must be a string.'); + } + return this; + } + + /** + * Sets the start date for the job. + * @param {string} start - The start date as a string. + * @returns {Job} - The current job instance (for chaining). + */ + setStart(start: string): Job { + this.start = start; + return this; + } + + /** + * Sets the end date for the job. + * @param {string} end - The end date as a string. + * @returns {Job} - The current job instance (for chaining). + */ + setEnd(end: string): Job { + this.end = end; + return this; + } + + /** + * Sets the repetition frequency for the job. + * @param {string} every - The repetition frequency. + * @returns {Job} - The current job instance (for chaining). + */ + setEvery(every: string): Job { + this.every = every; + return this; + } + + /** + * Sets the condition for job execution. + * @param {string} ifCondition - The condition as a string. + * @returns {Job} - The current job instance (for chaining). + */ + setIfCondition(ifCondition: string): Job { + this.ifCondition = ifCondition; + return this; + } + + /** + * Creates the job in MindsDB. + * @returns {Promise} - Resolves when the job is created. + * @throws {MindsDbError} - If job creation fails. + */ + async createJob(): Promise { + if (this.queries.length === 0) { + throw new MindsDbError('No queries added to the job.'); + } + const queryStr = this.queries.join(';\n'); + await this.jobsApiClient.createJob( + this.name, + this.project, + queryStr, + this.start, + this.end, + this.every, + this.ifCondition + ); + } + + /** + * Deletes the job from MindsDB. + * @returns {Promise} - Resolves when the job is deleted. + * @throws {MindsDbError} - If job deletion fails. + */ + async deleteJob(): Promise { + await this.jobsApiClient.deleteJob(this.name, this.project); + } + + /** + * Drops (deletes) a job from MindsDB by name. + * @param {string} name - Name of the job to drop. + * @param {string} project - Project the job belongs to. + * @returns {Promise} - Resolves when the job is dropped. + * @throws {MindsDbError} - Something went wrong while dropping the job. + */ + async dropJob(name: string, project: string = "mindsdb"): Promise { + await this.jobsApiClient.dropJob(name, project); + } +} diff --git a/src/jobs/jobsApiClient.ts b/src/jobs/jobsApiClient.ts new file mode 100644 index 0000000..b587ee9 --- /dev/null +++ b/src/jobs/jobsApiClient.ts @@ -0,0 +1,57 @@ +import Job from './job'; + +/** + * Abstract class outlining Job API operations supported by the SDK. + */ +export default abstract class JobsApiClient { + /** + * Creates a new Job instance for building and creating a job. + * @param {string} name - Name of the job. + * @param {string} project - Project the job belongs to. + * @returns {Job} - A new Job instance. + */ + abstract create(name: string, project: string): Job; + + /** + * Internal method to create the job in MindsDB. + * @param {string} name - Name of the job to create. + * @param {string} project - Project the job will be created in. + * @param {string} query - Queries to be executed by the job. + * @param {string} [start] - Optional start date for the job. + * @param {string} [end] - Optional end date for the job. + * @param {string} [every] - Optional repetition frequency. + * @param {string} [ifCondition] - Optional condition for job execution. + * @returns {Promise} - Resolves when the job is created. + * @throws {MindsDbError} - Something went wrong while creating the job. + */ + abstract createJob( + name: string, + project: string, + query: string, + start?: string, + end?: string, + every?: string, + ifCondition?: string + ): Promise; + + /** + * Internal method to delete the job in MindsDB. + * @param {string} name - Name of the job to delete. + * @param {string} project - Project the job will be deleted. + * @returns {Promise} - Resolves when the job is deleted. + * @throws {MindsDbError} - Something went wrong while deleting the job. + */ + abstract deleteJob( + name: string, + project: string + ): Promise + + /** + * Drops (deletes) a job from MindsDB by name. + * @param {string} name - Name of the job to drop. + * @param {string} project - Project the job belongs to. + * @returns {Promise} - Resolves when the job is dropped. + * @throws {MindsDbError} - Something went wrong while dropping the job. + */ + abstract dropJob(name: string, project: string): Promise; +} diff --git a/src/jobs/jobsModule.ts b/src/jobs/jobsModule.ts new file mode 100644 index 0000000..0ea5be8 --- /dev/null +++ b/src/jobs/jobsModule.ts @@ -0,0 +1,3 @@ +import JobsRestApiClient from './jobsRestApiClient'; + +export default { JobsRestApiClient }; diff --git a/src/jobs/jobsRestApiClient.ts b/src/jobs/jobsRestApiClient.ts new file mode 100644 index 0000000..aae360e --- /dev/null +++ b/src/jobs/jobsRestApiClient.ts @@ -0,0 +1,108 @@ +import mysql from 'mysql'; +import { MindsDbError } from '../errors'; + +import SqlApiClient from '../sql/sqlApiClient'; +import Job from './job'; +import JobsApiClient from './jobsApiClient'; + +export default class JobsRestApiClient extends JobsApiClient { + sqlClient: SqlApiClient; + + /** + * @param {SqlApiClient} sqlClient - SQL API client to send all SQL query requests. + */ + constructor(sqlClient: SqlApiClient) { + super(); + this.sqlClient = sqlClient; + } + + /** + * Creates a new Job instance for building and creating a job. + * @param {string} name - Name of the job. + * @param {string} project - Project the job belongs to. + * @returns {Job} - A new Job instance. + */ + override create(name: string, project: string = "mindsdb"): Job { + return new Job(this, name, project); + } + + /** + * Internal method to create the job in MindsDB. + * @param {string} name - Name of the job to create. + * @param {string} project - Project the job will be created in. + * @param {string} query - Queries to be executed by the job. + * @param {string} [start] - Optional start date for the job. + * @param {string} [end] - Optional end date for the job. + * @param {string} [every] - Optional repetition frequency. + * @param {string} [ifCondition] - Optional condition for job execution. + * @returns {Promise} - Resolves when the job is created. + * @throws {MindsDbError} - Something went wrong while creating the job. + */ + override async createJob( + name: string, + project: string, + query: string, + start?: string, + end?: string, + every?: string, + ifCondition?: string + ): Promise { + let createJobQuery = `CREATE JOB ${mysql.escapeId(project)}.${mysql.escapeId(name)} (\n${query}\n)`; + + if (start) { + createJobQuery += `\nSTART '${start}'`; + } + if (end) { + createJobQuery += `\nEND '${end}'`; + } + if (every) { + createJobQuery += `\nEVERY ${every}`; + } + if (ifCondition) { + createJobQuery += `\nIF (\n${ifCondition}\n)`; + } + + const sqlQueryResult = await this.sqlClient.runQuery(createJobQuery); + if (sqlQueryResult.error_message) { + throw new MindsDbError(sqlQueryResult.error_message); + } + } + + /** + * Internal method to delete the job in MindsDB. + * @param {string} name - Name of the job to delete. + * @param {string} project - Project the job belongs to. + * @returns {Promise} - Resolves when the job is deleted. + * @throws {MindsDbError} - Something went wrong while deleting the job. + */ + override async deleteJob( + name: string, + project: string + ): Promise { + const dropJobQuery = `DROP JOB ${mysql.escapeId(project)}.${mysql.escapeId(name)};`; + + const sqlQueryResult = await this.sqlClient.runQuery(dropJobQuery); + if (sqlQueryResult.error_message) { + throw new MindsDbError(sqlQueryResult.error_message); + } + } + + /** + * Internal method to deleting the job in MindsDB with users providing a name + * @param {string} name - Name of the job to delete. + * @param {string} project - Project the job belongs to. + * @returns {Promise} - Resolves when the job is deleted. + * @throws {MindsDbError} - Something went wrong while deleting the job. + */ + override async dropJob( + name: string, + project: string = "mindsdb" + ): Promise { + const dropJobQuery = `DROP JOB ${mysql.escapeId(project)}.${mysql.escapeId(name)};`; + + const sqlQueryResult = await this.sqlClient.runQuery(dropJobQuery); + if (sqlQueryResult.error_message) { + throw new MindsDbError(sqlQueryResult.error_message); + } + } +}