Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Hacktoberfest] Job functionality #97

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -116,6 +118,7 @@ export default {
Projects,
Tables,
Views,
Jobs,
MLEngines,
Callbacks,
};
Expand Down
139 changes: 139 additions & 0 deletions src/jobs/job.ts
Original file line number Diff line number Diff line change
@@ -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<void>} - Resolves when the job is created.
* @throws {MindsDbError} - If job creation fails.
*/
async createJob(): Promise<void> {
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<void>} - Resolves when the job is deleted.
* @throws {MindsDbError} - If job deletion fails.
*/
async deleteJob(): Promise<void> {
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<void>} - Resolves when the job is dropped.
* @throws {MindsDbError} - Something went wrong while dropping the job.
*/
async dropJob(name: string, project: string = "mindsdb"): Promise<void> {
await this.jobsApiClient.dropJob(name, project);
}
}
57 changes: 57 additions & 0 deletions src/jobs/jobsApiClient.ts
Original file line number Diff line number Diff line change
@@ -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<void>} - 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<void>;

/**
* 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<void>} - Resolves when the job is deleted.
* @throws {MindsDbError} - Something went wrong while deleting the job.
*/
abstract deleteJob(
name: string,
project: string
): Promise<void>

/**
* 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<void>} - Resolves when the job is dropped.
* @throws {MindsDbError} - Something went wrong while dropping the job.
*/
abstract dropJob(name: string, project: string): Promise<void>;
}
3 changes: 3 additions & 0 deletions src/jobs/jobsModule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import JobsRestApiClient from './jobsRestApiClient';

export default { JobsRestApiClient };
108 changes: 108 additions & 0 deletions src/jobs/jobsRestApiClient.ts
Original file line number Diff line number Diff line change
@@ -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<void>} - 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<void> {
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<void>} - Resolves when the job is deleted.
* @throws {MindsDbError} - Something went wrong while deleting the job.
*/
override async deleteJob(
name: string,
project: string
): Promise<void> {
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<void>} - Resolves when the job is deleted.
* @throws {MindsDbError} - Something went wrong while deleting the job.
*/
override async dropJob(
name: string,
project: string = "mindsdb"
): Promise<void> {
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);
}
}
}