@dxdeveloperexperience/hygie
Version:
Hygie is an easy-to-use Open-Source REST API allowing you to interact with GIT events. This NestJS API expose a set of customizable rules to automate your project's life cycle.
193 lines (174 loc) • 6.01 kB
text/typescript
import { Injectable, HttpService, HttpStatus } from '@nestjs/common';
import { NestSchedule } from '@dxdeveloperexperience/nest-schedule';
import { Schedule } from './schedule';
import {
CronStandardClass,
convertCronType,
CronType,
CronInterface,
} from './cron.interface';
import { GithubService } from '../github/github.service';
import { GitlabService } from '../gitlab/gitlab.service';
import { RulesService } from '../rules/rules.service';
import { SchedulerException } from '../exceptions/scheduler.exception';
import {
checkCronExpression,
getMatchingFiles,
getCronFileName,
} from './utils';
import { CronExpressionException } from '../exceptions/cronExpression.exception';
import { DataAccessService } from '../data_access/dataAccess.service';
import { HttpResponse } from '../utils/httpResponse';
import { Utils } from '../utils/utils';
import { RemoteConfigUtils } from '../remote-config/utils';
import { Constants } from '../utils/constants';
import { logger } from '../logger/logger.service';
import { Webhook, WebhookCommit } from '../webhook/webhook';
import { GitEventEnum } from '../webhook/utils.enum';
()
export class ScheduleService {
schedules: NestSchedule[] = new Array<NestSchedule>();
readonly MAX_SCHEDULES: number = 1000;
constructor(
private readonly githubService: GithubService,
private readonly gitlabService: GitlabService,
private readonly rulesService: RulesService,
private readonly httpService: HttpService,
private readonly dataAccessService: DataAccessService,
) {}
/**
* Create a new Schedule and add it to the list if `MAX_SCHEDULES` is not reached
*/
async createSchedule(cron: CronStandardClass): Promise<Schedule> {
if (this.schedules.length < this.MAX_SCHEDULES) {
// Download file
try {
await RemoteConfigUtils.downloadRulesFile(
this.dataAccessService,
this.httpService,
this.githubService,
this.gitlabService,
cron.projectURL,
cron.filename,
).catch(e => {
throw e;
});
} catch (e) {
logger.error(e, {
project: cron.projectURL,
location: 'ScheduleService',
});
throw e;
}
// Get CRON Expression if defined in the cron-*.rulesrc file
const remoteRepository =
'remote-rules/' +
Utils.getRepositoryFullName(cron.projectURL) +
'/.hygie';
const conf = await Utils.parseYAMLFile(
await this.dataAccessService.readRule(
`${remoteRepository}/${cron.filename}`,
),
);
const options = conf.options;
if (
typeof options !== 'undefined' &&
typeof options.cron !== 'undefined'
) {
cron.expression = options.cron;
} else {
cron.expression = Constants.cronExpression;
}
if (!checkCronExpression(cron.expression)) {
throw new CronExpressionException(
'Incorrect Cron Expression! You can not generate more than 1 cron job per hour.',
);
}
const newSchedule = new Schedule(
this.githubService,
this.gitlabService,
this.rulesService,
this.httpService,
this.dataAccessService,
cron,
);
this.addSchedule(newSchedule);
return newSchedule;
} else {
throw new SchedulerException('MAX_SCHEDULES reached!');
}
}
/**
* Add Schedule to the list
*/
addSchedule(schedule: NestSchedule): void {
this.schedules.push(schedule);
}
async createCronJobs(cronType: CronType): Promise<HttpResponse> {
let responseString: string = '';
let schedule: Schedule;
let cronStandardArray: CronStandardClass[];
try {
cronStandardArray = convertCronType(cronType);
} catch (e) {
throw new HttpResponse(HttpStatus.PRECONDITION_FAILED, e.message);
}
for (let index = 0; index < cronStandardArray.length; index++) {
// Need a for loop because Async/Wait does not work in ForEach
const cron = cronStandardArray[index];
try {
schedule = await this.createSchedule(cron);
responseString += `Schedule ${schedule.id} successfully created\n`;
} catch (e) {
throw new HttpResponse(
HttpStatus.UNAUTHORIZED,
`${responseString}\n${e.message}`,
);
}
}
return new HttpResponse(HttpStatus.OK, responseString);
}
/**
* Check all created/updated/deleted cron files to update the Scheduler
*/
processCronFiles(webhook: Webhook) {
if (webhook.gitEvent !== GitEventEnum.Push) {
return;
}
const commits: WebhookCommit[] = webhook.getAllCommits();
const projectURL: string = webhook.getCloneURL();
const allAddedCronFiles: string[] = getMatchingFiles(commits, 'added');
const allUpdatedCronFiles: string[] = getMatchingFiles(commits, 'modified');
const allRemovedCronFiles: string[] = getMatchingFiles(commits, 'removed');
const addOrUpdate: string[] = allAddedCronFiles.concat(allUpdatedCronFiles);
if (addOrUpdate.length > 0) {
const addOrUpdateCrons: CronInterface[] = addOrUpdate.map(a => {
return {
filename: getCronFileName(a),
projectURL,
};
});
this.createCronJobs(addOrUpdateCrons).catch(err =>
logger.error(err, {
location: 'processCronFiles',
project: webhook.getCloneURL(),
}),
);
}
if (allRemovedCronFiles.length > 0) {
allRemovedCronFiles.map(r => {
const filename: string = getCronFileName(r);
const cron: string = `remote-crons/${Utils.getRepositoryFullName(
webhook.getCloneURL(),
)}/${filename}`;
logger.info('Removing ' + cron);
this.dataAccessService.deleteCron(cron).catch(err =>
logger.error(err, {
location: 'processCronFiles',
project: webhook.getCloneURL(),
}),
);
});
}
}
}