import { Injectable } from '@nestjs/common'; import { Rule } from './rule.class'; import { RunnablesService } from '../runnables/runnables.service'; import { Webhook } from '../webhook/webhook'; import { RuleResult } from './ruleResult'; import { RulesOptions } from './rules.options'; import { Group } from './group.class'; import { logger } from '../logger/logger.service'; import { GroupResult } from './groupResult'; import { DataAccessService } from '../data_access/dataAccess.service'; import { Utils } from '../utils/utils'; @Injectable() export class RulesService { constructor( private readonly runnableService: RunnablesService, private readonly dataAccessService: DataAccessService, private readonly rulesClasses: Rule[] = [], ) {} async getConfiguration( remoteRepository: string, ruleFile: string, ): Promise { return await Utils.parseRuleFile( await this.dataAccessService.readRule(`${remoteRepository}/${ruleFile}`), ); } getRulesConfiguration(configuration: any): Rule[] { return configuration.rules || []; } getGroupsConfiguration(configuration: any): Group[] { const groupsConfig = configuration.groups || []; return groupsConfig.map(g => { const group = new Group(); group.groupName = g.groupName; group.onError = g.onError || []; group.onSuccess = g.onSuccess || []; group.onBoth = g.onBoth || []; group.rules = g.rules; return group; }); } getRulesOptions(configuration: any): RulesOptions { return new RulesOptions(configuration.options); } getRule(ruleConfig): Rule { return this.rulesClasses.find(r => r.name === ruleConfig.name); } async testRules( webhook: Webhook, remoteRepository: string, ruleFile: string, ): Promise { return new Promise(async (resolve, reject) => { const configuration = await this.getConfiguration( remoteRepository, ruleFile, ); const rules: Rule[] = this.getRulesConfiguration(configuration); const groups: Group[] = this.getGroupsConfiguration(configuration); const rulesOptions: RulesOptions = this.getRulesOptions(configuration); const BreakException = {}; const results: RuleResult[] = new Array(); // Individual rules if (rulesOptions.enableRules) { try { logger.info('### TRY RULES ###', { project: webhook.getCloneURL(), }); for (let index = 0; index < rules.length; index++) { // Need a for loop because Async/Wait does not work in ForEach const ruleConfig = rules[index]; const r = this.getRule(ruleConfig); if (r.isEnabled(webhook, ruleConfig)) { const ruleResult: RuleResult = await r.validate( webhook, ruleConfig, results, ); // Some rules return `null` when there are enabled but ignored due to interne business rule if (ruleResult !== null) { results.push(ruleResult); this.runnableService .executeRunnableFunctions(ruleResult, ruleConfig) .catch(err => logger.error(err)); if (!rulesOptions.executeAllRules && !ruleResult.validated) { throw BreakException; } } } } } catch (e) { if (e !== BreakException) { logger.error(e, { project: webhook.getCloneURL(), location: 'RulesService', }); } } } // Grouped rules if (rulesOptions.enableGroups) { try { logger.info('### TRY GROUPS ###', { project: webhook.getCloneURL(), }); for (let indexGroup = 0; indexGroup < groups.length; indexGroup++) { // Need a for loop because Async/Wait does not work in ForEach const g = groups[indexGroup]; const groupResults: GroupResult[] = new Array(); for ( let indexRules = 0; indexRules < g.rules.length; indexRules++ ) { // Need a for loop because Async/Wait does not work in ForEach const ruleConfig = g.rules[indexRules]; const r = this.getRule(ruleConfig); if (r.isEnabled(webhook, ruleConfig)) { const ruleResult: RuleResult = await r.validate( webhook, ruleConfig, ); // Some rules return `null` when there are enabled but ignored due to interne business rule if (ruleResult !== null) { results.push(ruleResult); if (rulesOptions.allRuleResultInOne) { groupResults.push({ name: ruleConfig.name, ruleResult, }); } else { this.runnableService .executeRunnableFunctions(ruleResult, g) .catch(err => logger.error(err)); } if ( !rulesOptions.executeAllRules && !rulesOptions.allRuleResultInOne && !ruleResult.validated ) { throw BreakException; } } } } // If all rules have been tested, we can execute runnable functions with the result of previous rules if (rulesOptions.allRuleResultInOne) { if (groupResults.length > 0) { const ruleResult: RuleResult = new RuleResult( webhook.getGitApiInfos(), ); ruleResult.data = groupResults; this.runnableService .executeRunnableFunctions(ruleResult, g) .catch(err => logger.error(err)); } } } } catch (e) { logger.error(e, { project: webhook.getCloneURL(), location: 'RulesService', }); } } resolve(results); }); } }