workers/bag_validator.js

const { Constants } = require('../core/constants');
const { Context } = require('../core/context');
const dateFormat = require('dateformat');
const fs = require('fs');
const { OperationResult } = require('../core/operation_result');
const { Validator } = require('../bagit/validator');
const { Worker } = require('./worker');

/**
 * BagValidator validates a bag. It catches events from the
 * underlying {@link Validator}, wraps them in {@link JobStatus}
 * objects, and writes them to STDOUT and STDERR to communicate
 * with the parent process.
 *
 * param {Job} - The job to run. The job object must contain a
 * {@link BagItProfile} and a {@link ValidationOperation} to be
 * valid.
 *
 * @param {Job}
 */
class BagValidator extends Worker {

    constructor(job) {
        super('validate');
        this.job = job;
    }

    /**
     * This runs the job's validation operation, validating the bag.
     * It returns a promise.
     *
     * @returns {Promise}
     */
    run() {
        var bagValidator = this;
        this.initOpResult();

        // Return a failed promise if we get invalid params.
        let errors = this.validateParams();
        if (errors.length > 0) {
            return new Promise(function(resolve, reject) {
                bagValidator.job.validationOp.result.finish(errors);
                reject(bagValidator.job.validationOp.result);
            });
        }

        return new Promise(function(resolve, reject) {
            let validator = new Validator(
                bagValidator.job.validationOp.pathToBag,
                bagValidator.job.bagItProfile);
            validator.on('error', function(err) {
                if (typeof err == 'string') {
                    bagValidator.job.validationOp.result.finish(err);
                    bagValidator.runtimeError('validate', [err], null);
                } else {
                    bagValidator.job.validationOp.result.finish(err.toString());
                    bagValidator.runtimeError('validate', null, err);
                }
                reject(bagValidator.job.validationOp.result);
            });
            validator.on('end', function() {
                bagValidator.job.validationOp.result.finish();
                if (validator.errors.length > 0) {
                    bagValidator.job.validationOp.result.errors = validator.errors;
                    bagValidator.completedWithError(validator.errors);
                } else {
                    bagValidator.completedSuccess(Context.y18n.__('Bag is valid'));
                }
                resolve(bagValidator.job.validationOp.result);
            });
            validator.on('task', function(taskDesc) {
                bagValidator.info(taskDesc.op, Constants.OP_IN_PROGRESS, taskDesc.path, taskDesc.percentComplete, false);
            });
            validator.validate();
        });
    }

    /**
     * This initializes the {@link OperationResult} on the job's
     * {@link ValidationOperation}.
     *
     * @private
     */
    initOpResult() {
        if (this.job.validationOp.result == null) {
            this.job.validationOp.result = new OperationResult('validation', 'DART BagIt validator');
        }
        let result = this.job.validationOp.result;
        result.filepath = this.job.validationOp.pathToBag;
        result.start();
        try {
            let stats = fs.statSync(this.job.validationOp.pathToBag);
            if (stats.isFile()) {
                result.filesize = stats.size;
                result.fileMtime = stats.mtime;
            }
        } catch (ex) {
            result.finish(Context.y18n.__(Context.y18n.__('Error gathering info about bag: %s'), ex.message));
        }
    }

    /**
     * This validates that the job object passed into the constructor
     * contains sufficient information for the validator to do its
     * work. The job must include a valid {@link ValidationOperation}
     * and a BagItProfile. Returns an array of error messages.
     *
     * @returns {Array<string>}
     * @private
     */
    validateParams() {
        this.job.validationOp.validate();
        let errors = Object.values(this.job.validationOp.errors);
        if (!this.job.bagItProfile) {
            errors.push(Context.y18n.__("Cannot validate bag because job has no BagItProfile."));
        } else if (!this.job.bagItProfile.validate()) {
            for (let err of Object.values(this.job.bagItProfile.errors)) {
                errors.push(err);
            }
        }
        return errors;
    }
}

module.exports.BagValidator = BagValidator;