Jam3/adviser

View on GitHub
src/core/rule/rule.js

Summary

Maintainability
A
2 hrs
Test Coverage
/**
 * @fileoverview Adviser Rules.
 *
 */

'use strict';

const debug = require('debug')('adviser:rule');

const RuleLifeCycleEnum = require('./lifecycle/rule-lifecycle-enum');
const RuleContext = require('./lifecycle/rule-context');
const RuleSandbox = require('./lifecycle/rule-sandbox');
const RuleFeedback = require('./lifecycle/rule-feedback');
const RuleStatusEnum = require('./lifecycle/rule-status-enum');
const SeverityEnum = require('../config/severity-enum');

/**
 * Wrap individual rules in the core
 *
 * @class Rule
 */
class Rule {
  constructor(id, fullRuleName, pluginName, core, severity, options) {
    this.id = id.toLowerCase();
    this.fullRuleName = fullRuleName;
    this.pluginName = pluginName.toLowerCase();

    this.sharedContext = null;

    this.severity = severity;
    this.options = options;

    this.core = core;

    this.lifeCycleStatus = severity === SeverityEnum.Off ? RuleStatusEnum.Skipped : RuleStatusEnum.Idle;
  }

  /**
   * Get full scoped rule id, ex: [plugin name]/[rule id]
   *
   * @returns
   * @memberof Rule
   */
  getPluginScopedRuleId() {
    return `${this.pluginName}/${this.id}`;
  }

  /**
   * Add to the rule data that will be included in the context.
   *
   * @param {Object} sharedContext
   * @memberof Rule
   */
  addSharedContext(sharedContext) {
    this.sharedContext = sharedContext;
  }

  /**
   * Run the rule lifecycle
   *
   * @param {Path} currentDirectory
   * @param {Function} reportCallback Engine report callback
   * @param {Function} asyncCallback Callback to be called once the execution is finished
   * @memberof Rule
   */
  async runLifeCycle(currentDirectory, verboseMode, reportCallback, asyncCallback) {
    if (this.severity === SeverityEnum.Off) {
      asyncCallback();
      return;
    }

    let instanceRule = null;
    let phase = RuleLifeCycleEnum.Init;

    const instanceContext = new RuleContext(
      this.id,
      this.pluginName,
      currentDirectory,
      null,
      this.options,
      this.severity,
      verboseMode,
      this.sharedContext
    );

    try {
      debug(`The engine started executing the lifecycle of the rule ${this.id}`);

      // 1. Init rules
      const DefinedRule = this.core;
      instanceRule = new DefinedRule(instanceContext);

      // 2. Run the rule
      phase = RuleLifeCycleEnum.Run;
      const sandbox = new RuleSandbox(reportCallback, instanceContext);
      await instanceRule.run(sandbox);

      // 3. Rule Execution Ended
      phase = RuleLifeCycleEnum.ruleExecutionEnded;
      const feedback = new RuleFeedback(RuleStatusEnum.Completed, phase);
      instanceRule.ruleExecutionEnded(feedback);
      this.lifeCycleStatus = RuleStatusEnum.Completed;
    } catch (error) {
      debug(`The rule ${this.id} lifecycle failed with error ${error}`);
      this.lifeCycleStatus = RuleStatusEnum.Failed;

      // 4. Rule Execution Failed
      const feedback = new RuleFeedback(RuleStatusEnum.Failed, phase);

      try {
        if (instanceRule !== null) {
          instanceRule.ruleExecutionFailed(feedback, error);
        }
      } catch (executionFailedError) {
        debug(`The "ruleExecutionFailed" lifecycle failed with error ${executionFailedError}`);
      }
    } finally {
      debug(`The engine finished executing the lifecycle of the rule ${this.id}`);
      asyncCallback();
    }
  }
}

module.exports = Rule;