diff --git a/services/config-validator.js b/services/config-validator.js index b56c4d35..48c1e827 100644 --- a/services/config-validator.js +++ b/services/config-validator.js @@ -1,8 +1,11 @@ -/* eslint-disable no-console */ -/* Script that validates the conf.yml file against Dashy's schema, and outputs any issues */ -const Ajv = require('ajv'); -const yaml = require('js-yaml'); -const fs = require('fs'); +/** + * Checks that conf.yml is present + parsable, then validates it against the schema + * Prints detailed info about any errors or warnings to help the user fix the issue + */ + +const fs = require('fs'); // For opening + reading files +const yaml = require('js-yaml'); // For parsing YAML +const Ajv = require('ajv'); // For validating with schema const schema = require('../src/utils/ConfigSchema.json'); @@ -17,32 +20,29 @@ const validatorOptions = { const ajv = new Ajv(validatorOptions); /* Message printed when validation was successful */ -const successMsg = () => '\x1b[1m\x1b[32mNo issues found, your configuration is valid :)\x1b[0m\n'; +const successMsg = () => '\x1b[1m\x1b[32m✔️ Config file is valid, no issues found\x1b[0m\n'; /* Just a wrapper to system's console.log */ -const logToConsole = (msg) => { console.log(msg); }; +const logToConsole = (msg) => { console.log(msg || '\n'); }; // eslint-disable-line no-console /* Formats error message. ready for printing to the console */ const errorMsg = (output) => { const warningFont = '\x1b[103m\x1b[34m'; const line = `${warningFont}${new Array(42).fill('━').join('')}\x1b[0m`; + const formatParams = (params) => { + if (params.additionalProperty) return `(${params.additionalProperty})`; + return ''; + }; let msg = `\n${line}\n${warningFont} Warning: ${output.length} ` + `issue${output.length > 1 ? 's' : ''} found in config file \x1b[0m\n${line}\n`; output.forEach((details, index) => { - msg += `${'\x1b[36m'}${index + 1}. ${details.keyword} ${details.message} ` - + `in ${details.instancePath}\x1b[0m\n`; + msg += `${'\x1b[36m'}${index + 1}. \x1b[4m${details.instancePath}\x1b[0m\x1b[36m ` + + `${details.message} ${formatParams(details.params)}\x1b[0m\n`; }); return msg; }; -/* Error message printed when the file could not be opened */ -const bigError = () => { - const formatting = '\x1b[30m\x1b[43m'; - const line = `${formatting}${new Array(38).fill('━').join('')}\x1b[0m\n`; - const msg = `${formatting} Error, unable to validate 'conf.yml' \x1b[0m\n`; - return `\n${line}${msg}${line}\n`; -}; - +/* Sets valid status as environmental variable */ const setIsValidVariable = (isValid) => { process.env.VUE_APP_CONFIG_VALID = isValid; }; @@ -60,16 +60,49 @@ const validate = (config) => { } }; -try { +/* Error message printed when the file could not be opened */ +const bigError = () => { + const formatting = '\x1b[30m\x1b[43m'; + const line = `${formatting}${new Array(38).fill('━').join('')}\x1b[0m\n`; + const msg = `${formatting} Error, unable to validate 'conf.yml' \x1b[0m\n`; + return `\n${line}${msg}${line}`; +}; + +/* Given an error object, prints helpful info to the user */ +const printFileReadError = (e) => { + let customError = ''; + if (e.mark) { // YAML syntax error + customError = `\x1b[33m\x1b[4m⚠️ Error on line ${e.mark.line}, column ${e.mark.column}: ` + + `${e.reason}\x1b[0m\n\n${e.mark.snippet}\n\x1b[0m` + + '\n\x1b[36m ℹ️ You might find it helpful to use a YAML validator' + + ', like: \x1b[4mhttps://yamlchecker.com/\x1b[0m\n'; + } + if (e.code === 'ENOENT') { // File not found error + customError = `\x1b[33m⚠️ Config file could not be found at ${e.path}\x1b[0m\n`; + } + if (e.code === 'EISDIR') { // Not a file + customError = '\x1b[33m⚠️ Config needs to be a file, but found a directory instead \x1b[0m\n'; + } + if (e.code === 'EACCES' || e.code === 'EPERM') { // File permissions error + customError = '\x1b[33m⚠️ Permission denied \x1b[0m\n'; + } + logToConsole(customError); + if (customError === '') { // Unknown error, print stack trace + const moreInfo = 'Ensure that your config file is present, readable, and valid YAML. ' + + 'If this issue persists, you can get support by raising a ticket on GitHub. ' + + 'Please include the following stack trace'; + logToConsole(moreInfo); + // eslint-disable-next-line no-console + console.warn('\x1b[33mStack Trace for config-validator.js:\x1b[0m\n', e); + logToConsole(); + } +}; + +try { // Try to open and parse the YAML file const config = yaml.load(fs.readFileSync('./public/conf.yml', 'utf8')); validate(config); } catch (e) { // Something went very wrong... setIsValidVariable(false); logToConsole(bigError()); - logToConsole('Please ensure that your config file is present, ' - + 'has the correct access rights and is parsable. ' - + 'If this warning persists, it may be an issue with the ' - + 'validator function. Please raise an issue, and include the following stack trace:\n'); - console.warn('\x1b[33mStack Trace for config-validator.js:\x1b[0m\n', e); - logToConsole('\n\n'); + printFileReadError(e); }