diff --git a/lib/util/utils.js b/lib/util/utils.js index b9846286..bcdbf878 100644 --- a/lib/util/utils.js +++ b/lib/util/utils.js @@ -6,6 +6,7 @@ var fs = require('fs'), util = require('util'), path = require('path'), jsonPointer = require('json-pointer'), + YAML = require('js-yaml'), log = require('./logging'), request = require('request'); @@ -15,7 +16,7 @@ exports = module.exports; * Caches the json docs that were successfully parsed by exports.parseJson(). This avoids, fetching them again. * key: docPath * value: parsed doc in JSON format - */ + */ exports.docCache = {}; @@ -27,7 +28,7 @@ exports.clearCache = function clearCache() { * Removes byte order marker. This catches EF BB BF (the UTF-8 BOM) * because the buffer-to-string conversion in `fs.readFile()` * translates it to FEFF, the UTF-16 BOM. - */ + */ exports.stripBOM = function stripBOM(content) { if (Buffer.isBuffer(content)) { content = content.toString(); @@ -61,13 +62,14 @@ exports.parseJson = function parseJson(specPath) { if (specPath.startsWith('https://github')) { specPath = specPath.replace(/^https:\/\/(github.com)(.*)blob\/(.*)/ig, 'https://raw.githubusercontent.com$2$3'); } - let res = exports.makeRequest({ url: specPath, errorOnNon200Response: true}); + let res = exports.makeRequest({ url: specPath, errorOnNon200Response: true }); exports.docCache[specPath] = res; return res; } else { //local filepath try { - result = JSON.parse(exports.stripBOM(fs.readFileSync(specPath, 'utf8'))); + let fileContent = exports.stripBOM(fs.readFileSync(specPath, 'utf8')); + let result = exports.parseContent(specPath, fileContent); exports.docCache[specPath] = result; return Promise.resolve(result); } catch (err) { @@ -77,11 +79,34 @@ exports.parseJson = function parseJson(specPath) { } }; +/* + * Provides a parsed JSON from the given content. + * + * @param {string} filePath - A local file path or a (github) url to the swagger spec. + * + * @param {string} fileContent - The content to be parsed. + * + * @returns {object} jsonDoc - Parsed document in JSON format. + */ +exports.parseContent = function parseContent(filePath, fileContent) { + let result = null; + if (/.*\.json$/ig.test(filePath)) { + result = JSON.parse(fileContent); + } else if (/.*\.ya?ml$/ig.test(filePath)) { + result = YAML.safeLoad(fileContent); + } else { + let msg = `We currently support "*.json" and "*.yaml | *.yml" file formats for validating swaggers.\n` + + `The current file extension in "${filePath}" is not supported.`; + throw new Error(msg); + } + return result; +}; + /* * A utility function to help us acheive stuff in the same way as async/await but with yield statement and generator functions. * It waits till the task is over. * @param {function} A generator function as an input - */ + */ exports.run = function run(genfun) { // instantiate the generator object var gen = genfun(); @@ -118,28 +143,29 @@ exports.run = function run(genfun) { * @param {boolean} options.errorOnNon200Response If true will reject the promise with an error if the response statuscode is not 200. * * @return {Promise} promise - A promise that resolves to the responseBody or rejects to an error. - */ + */ exports.makeRequest = function makeRequest(options) { - var promise = new Promise(function (resolve, reject) { request(options, function (err, response, responseBody) { if (err) { - return reject(err); + reject(err); } if (options.errorOnNon200Response && response.statusCode !== 200) { var msg = `StatusCode: "${response.statusCode}", ResponseBody: "${responseBody}."`; - return reject(new Error(msg)); + reject(new Error(msg)); } - let res; + let res = responseBody; try { - res = typeof responseBody.valueOf() === 'string'? JSON.parse(exports.stripBOM(responseBody)) : responseBody; + if (typeof responseBody.valueOf() === 'string') { + res = exports.parseContent(options.url, responseBody); + } } catch (error) { - let msg = `An error occurred while executing JSON.parse() on the responseBody. ${util.inspect(error, {depth: null})}.` + let msg = `An error occurred while parsing the responseBody. ${util.inspect(error, { depth: null })}.` let e = new Error(msg); - return reject(e); + reject(e); } - - return resolve(res); + + resolve(res); }); }); return promise; @@ -210,7 +236,7 @@ exports.parseReferenceInSwagger = function parseReferenceInSwagger(reference) { } let result = {}; - if (reference.includes('#')) { + if (reference.includes('#')) { //local reference in the doc if (reference.startsWith('#/')) { result.localReference = {}; @@ -228,7 +254,7 @@ exports.parseReferenceInSwagger = function parseReferenceInSwagger(reference) { //we are assuming that the string is a relative filePath result.filePath = reference; } - + return result; }; @@ -244,7 +270,7 @@ exports.parseReferenceInSwagger = function parseReferenceInSwagger(reference) { * @param variable number of arguments and all the arguments must be of type string. Similar to the API * provided by path.join() https://nodejs.org/dist/latest-v6.x/docs/api/path.html#path_path_join_paths * @return {string} resolved path - */ + */ exports.joinPath = function joinPath() { let finalPath = path.join.apply(path, arguments); finalPath = finalPath.replace(/\\/gi, '/'); @@ -273,9 +299,9 @@ exports.parseJsonWithPathFragments = function parseJsonWithPathFragments() { * @param {object} target The object to be merged into * * @returns {object} target - Returns the merged target object. - */ + */ exports.mergeObjects = function mergeObjects(source, target) { - Object.keys(source).forEach(function(key) { + Object.keys(source).forEach(function (key) { target[key] = source[key]; }); return target; @@ -289,7 +315,7 @@ exports.mergeObjects = function mergeObjects(source, target) { * @param {string} ptr The json reference pointer * * @returns {any} result - Returns the value that the ptr points to, in the doc. - */ + */ exports.getObject = function getObject(doc, ptr) { let result; try { @@ -309,7 +335,7 @@ exports.getObject = function getObject(doc, ptr) { * * @param {any} value The value that needs to be set at the * location provided by the ptr in the doc. - */ + */ exports.setObject = function setObject(doc, ptr, value) { let result; try { @@ -325,7 +351,7 @@ exports.setObject = function setObject(doc, ptr, value) { * @param {object} doc The source object. * * @param {string} ptr The json reference pointer. - */ + */ exports.removeObject = function removeObject(doc, ptr) { let result; try { diff --git a/package.json b/package.json index 7abadaf0..48c6dfc5 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "dependencies": { "azure-arm-resource": "^1.6.1-preview", "json-pointer": "^0.6.0", + "js-yaml": "^3.8.2", "moment": "^2.14.1", "ms-rest": "^1.15.3", "ms-rest-azure": "^1.15.3",