xops-pat
Version:
Procedure generator for on space operations
193 lines (151 loc) • 5.04 kB
JavaScript
;
const fs = require('fs');
const path = require('path');
const YAML = require('yamljs');
const filenamify = require('filenamify');
const Actor = require('./actor.js');
const Column = require('./column.js');
const Task = require('./task.js');
const SpacewalkValidator = require('../schema/spacewalkValidator');
/**
* This function returns a Promise that provides the contents of a fetched URL
* as a UTF-8 string.
*
* @see: https://www.tomas-dvorak.cz/posts/nodejs-request-without-dependencies
*
* @param {string} url The URL to read
* @return {Promise} A promise
*/
function readUrlPromise(url) {
const lib = url.startsWith('https') ? require('https') : require('http');
return new Promise((resolve, reject) => {
const request = lib.get(url, (response) => {
const body = [];
response.setEncoding('utf8');
response.on('data', (chunk) => {
body.push(chunk);
});
response.on('end', () => {
resolve(body.toString());
});
});
request.on('error', (err) => {
reject(err);
});
});
}
function translatePath(procedureFilePath, taskFileName) {
// Look in tasks directory, sister to procedures directory
// Someday look in a directory provided by dependency manager, issue #21
const taskFilePath = path.join(
path.dirname(procedureFilePath),
'..',
'tasks',
taskFileName
);
// Validate & Load the yaml file!
if (!fs.existsSync(taskFilePath)) {
throw new Error(`Could not find task file ${taskFilePath}`);
}
return taskFilePath;
}
function mapActorToColumn(columnDefinition) {
// Create a mapping of actor --> column
const actorToColumn = {};
for (const col of columnDefinition) {
if (typeof col.actors === 'string') {
col.actors = [col.actors]; // array-ify
} else if (!Array.isArray(col.actors)) {
throw new Error('Procedure columns.actors must be array or string');
}
for (const actor of col.actors) {
actorToColumn[actor] = col.key;
}
}
return actorToColumn;
}
module.exports = class Procedure {
constructor() {
this.name = '';
this.filename = '';
this.actors = [];
this.columns = [];
this.tasks = [];
this.css = '';
this.actorToColumn = {};
}
getActorColumn(actor) {
if (this.actorToColumn[actor]) {
return this.actorToColumn[actor];
} else if (this.actorToColumn['*']) {
return this.actorToColumn['*']; // wildcard for all others
} else {
throw new Error(`Unknown column for actor ${actor}. Consider adding wildcard * actor to a column`);
}
}
/**
* Populates data, reading in the specified file.
*
* @param {*} fileName The full path to the YAML file
*
* @throws {Error} if an error is encountered parsing the file.
*/
async populateFromFile(fileName) {
try {
// Check if the file exists
if (!fs.existsSync(fileName)) {
throw new Error(`Could not find file ${fileName}`);
}
// Validate the input file
const spacewalkValidator = new SpacewalkValidator();
spacewalkValidator.validateProcedureSchemaFile(fileName);
// Load the YAML File
const procedureYaml = YAML.load(fileName, null, true);
// Save the procedure Name
this.name = procedureYaml.procedure_name;
this.filename = filenamify(this.name.replace(/\s+/g, '_'));
// Save the actors
for (var actorYaml of procedureYaml.actors) {
this.actors.push(new Actor(actorYaml));
}
for (var columnYaml of procedureYaml.columns) {
this.columns.push(new Column(columnYaml));
}
this.actorToColumn = mapActorToColumn(this.columns);
// Save the tasks
for (var taskYaml of procedureYaml.tasks) {
// Check that the task is a file
if (taskYaml.file) {
// Since the task file is in relative path to the procedure
// file, need to translate it!
const taskFileName = translatePath(fileName, taskYaml.file);
spacewalkValidator.validateTaskSchemaFile(taskFileName);
const loadedTaskYaml = YAML.load(taskFileName, null, true);
// Save the task!
this.tasks.push(new Task(loadedTaskYaml));
// Is this a URL?
} else if (taskYaml.url) {
// Wait for URL fetch to complete
// console.log('Reading task URL: ' + t.url);
const yamlString = await readUrlPromise(taskYaml.url);
// Validate the data read from url
spacewalkValidator.validateTaskSchemaString(yamlString);
// Parse the Task YAML
const loadedTaskYaml = YAML.parse(yamlString);
// Save the task!
this.tasks.push(new Task(loadedTaskYaml));
}
}
// Pull in css file if it is defined
if (procedureYaml.css) {
const cssFileName = translatePath(fileName, procedureYaml.css);
if (!fs.existsSync(cssFileName)) {
throw new Error(`Could not find css file ${cssFileName}`);
}
this.css = fs.readFileSync(cssFileName);
}
} catch (err) {
return err;
}
}
};