From f76e92ea556bcda9dc4c1a158a9e44851890f2a4 Mon Sep 17 00:00:00 2001 From: Bill Nguyen Ton Hoang Date: Thu, 24 Oct 2024 01:10:11 +0100 Subject: [PATCH 1/3] [feature] main structure for jobs in mindsdb-js-sdk --- src/jobs/job.ts | 21 +++++++++++++++++++++ src/jobs/jobApiClient.ts | 7 +++++++ src/jobs/jobsModule.ts | 0 src/jobs/jobsRestApiClient.ts | 0 4 files changed, 28 insertions(+) create mode 100644 src/jobs/job.ts create mode 100644 src/jobs/jobApiClient.ts create mode 100644 src/jobs/jobsModule.ts create mode 100644 src/jobs/jobsRestApiClient.ts diff --git a/src/jobs/job.ts b/src/jobs/job.ts new file mode 100644 index 0000000..22cdeec --- /dev/null +++ b/src/jobs/job.ts @@ -0,0 +1,21 @@ +import JobApiClient from "./jobApiClient"; + +/** + * Enables you to automate any pipeline to schedule any query at any frequency + */ +export default class Job { + jobsApiClient: JobApiClient; + + project_name: string; + + name: string; + + data: string; + + constructor(jobApiClient: JobApiClient) { + this.jobsApiClient = jobApiClient; + this.project_name = ""; + this.name = ""; + this.data = ""; + } +} \ No newline at end of file diff --git a/src/jobs/jobApiClient.ts b/src/jobs/jobApiClient.ts new file mode 100644 index 0000000..97ae348 --- /dev/null +++ b/src/jobs/jobApiClient.ts @@ -0,0 +1,7 @@ + +/** + * Abstract class outlining Job API operations supported by the SDK. + */ +export default abstract class JobApiClient { + +} \ No newline at end of file diff --git a/src/jobs/jobsModule.ts b/src/jobs/jobsModule.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/jobs/jobsRestApiClient.ts b/src/jobs/jobsRestApiClient.ts new file mode 100644 index 0000000..e69de29 From f139a0407df0af5c0fab412332ea3a873f084ea0 Mon Sep 17 00:00:00 2001 From: Bill Nguyen Ton Hoang Date: Thu, 24 Oct 2024 23:28:32 +0100 Subject: [PATCH 2/3] [feature] create job completed --- src/index.ts | 3 + src/jobs/job.ts | 122 ++++++++++++++++++++++++++++++---- src/jobs/jobApiClient.ts | 7 -- src/jobs/jobsApiClient.ts | 36 ++++++++++ src/jobs/jobsModule.ts | 3 + src/jobs/jobsRestApiClient.ts | 70 +++++++++++++++++++ 6 files changed, 222 insertions(+), 19 deletions(-) delete mode 100644 src/jobs/jobApiClient.ts create mode 100644 src/jobs/jobsApiClient.ts 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 index 22cdeec..d08b88c 100644 --- a/src/jobs/job.ts +++ b/src/jobs/job.ts @@ -1,21 +1,119 @@ -import JobApiClient from "./jobApiClient"; +import JobsApiClient from './jobsApiClient'; +import { MindsDbError } from '../errors'; /** - * Enables you to automate any pipeline to schedule any query at any frequency + * Represents a MindsDB job and provides methods to build and create it. */ export default class Job { - jobsApiClient: JobApiClient; + /** API client to use for executing job operations. */ + private jobsApiClient: JobsApiClient; - project_name: string; + /** Name of the job. */ + name: string; - name: string; + /** Project the job belongs to. */ + project: string; - data: string; + /** Accumulated queries to be executed by the job. */ + private queries: string[]; - constructor(jobApiClient: JobApiClient) { - this.jobsApiClient = jobApiClient; - this.project_name = ""; - this.name = ""; - this.data = ""; + /** 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 create(): Promise { + if (this.queries.length === 0) { + throw new MindsDbError('No queries added to the job.'); } -} \ No newline at end of file + const queryStr = this.queries.join(';\n'); + await this.jobsApiClient.createJob( + this.name, + this.project, + queryStr, + this.start, + this.end, + this.every, + this.ifCondition + ); + } +} diff --git a/src/jobs/jobApiClient.ts b/src/jobs/jobApiClient.ts deleted file mode 100644 index 97ae348..0000000 --- a/src/jobs/jobApiClient.ts +++ /dev/null @@ -1,7 +0,0 @@ - -/** - * Abstract class outlining Job API operations supported by the SDK. - */ -export default abstract class JobApiClient { - -} \ No newline at end of file diff --git a/src/jobs/jobsApiClient.ts b/src/jobs/jobsApiClient.ts new file mode 100644 index 0000000..e91205d --- /dev/null +++ b/src/jobs/jobsApiClient.ts @@ -0,0 +1,36 @@ +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; +} diff --git a/src/jobs/jobsModule.ts b/src/jobs/jobsModule.ts index e69de29..0ea5be8 100644 --- a/src/jobs/jobsModule.ts +++ 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 index e69de29..fb5ecb9 100644 --- a/src/jobs/jobsRestApiClient.ts +++ b/src/jobs/jobsRestApiClient.ts @@ -0,0 +1,70 @@ +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): 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); + } + } +} From 37f032ba9f1da6d294ff9d1c61911cda0e4ba917 Mon Sep 17 00:00:00 2001 From: Bill Nguyen Ton Hoang Date: Wed, 30 Oct 2024 00:21:40 +0000 Subject: [PATCH 3/3] [feature] deleting job completed --- src/jobs/job.ts | 22 ++++++++++++++++++- src/jobs/jobsApiClient.ts | 21 ++++++++++++++++++ src/jobs/jobsRestApiClient.ts | 40 ++++++++++++++++++++++++++++++++++- 3 files changed, 81 insertions(+), 2 deletions(-) diff --git a/src/jobs/job.ts b/src/jobs/job.ts index d08b88c..6c54f35 100644 --- a/src/jobs/job.ts +++ b/src/jobs/job.ts @@ -101,7 +101,7 @@ export default class Job { * @returns {Promise} - Resolves when the job is created. * @throws {MindsDbError} - If job creation fails. */ - async create(): Promise { + async createJob(): Promise { if (this.queries.length === 0) { throw new MindsDbError('No queries added to the job.'); } @@ -116,4 +116,24 @@ export default class Job { 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 index e91205d..b587ee9 100644 --- a/src/jobs/jobsApiClient.ts +++ b/src/jobs/jobsApiClient.ts @@ -33,4 +33,25 @@ export default abstract class JobsApiClient { 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/jobsRestApiClient.ts b/src/jobs/jobsRestApiClient.ts index fb5ecb9..aae360e 100644 --- a/src/jobs/jobsRestApiClient.ts +++ b/src/jobs/jobsRestApiClient.ts @@ -22,7 +22,7 @@ export default class JobsRestApiClient extends JobsApiClient { * @param {string} project - Project the job belongs to. * @returns {Job} - A new Job instance. */ - override create(name: string, project: string): Job { + override create(name: string, project: string = "mindsdb"): Job { return new Job(this, name, project); } @@ -67,4 +67,42 @@ export default class JobsRestApiClient extends JobsApiClient { 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); + } + } }