From 5e2685b887f1c17a9640558305ded43a746b2652 Mon Sep 17 00:00:00 2001 From: Raisel Melian Date: Thu, 24 Jan 2019 17:08:53 -0500 Subject: [PATCH] #2 convert YAML to JSON and vice-versa --- .gitignore | 1 + cli.js | 14 ++++- convert.js | 20 +++++++ lib/converter.js | 41 +++++++++++++ lib/parser.js | 18 +++--- lib/validator.js | 5 +- script.bash | 5 +- test/samples/asyncapi.json | 117 +++++++++++++++++++++++++++++++++++++ test/samples/asyncapi.yml | 10 +--- validate.js | 6 +- 10 files changed, 210 insertions(+), 27 deletions(-) create mode 100644 convert.js create mode 100644 lib/converter.js create mode 100644 test/samples/asyncapi.json diff --git a/.gitignore b/.gitignore index 7bbf772..ec6eb4a 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,4 @@ jspm_packages # Optional REPL history .node_repl_history +/.idea/ diff --git a/cli.js b/cli.js index 57849ea..273aff8 100755 --- a/cli.js +++ b/cli.js @@ -7,6 +7,7 @@ const program = require('commander'); const { version } = require('./package.json'); const validate = require('./validate'); +const convert = require('./convert'); function collect(val, item) { item.push(val); @@ -15,14 +16,21 @@ function collect(val, item) { program .version(version) - .usage('') - .option('-c, --config [configFile]', 'config file (containing JSON/YAML). See README for potential values.'); + .usage(''); + // .option('-c, --config [configFile]', 'config file (containing JSON/YAML). See README for potential values.'); program .command('validate ') - .description('validate Async-API documents') + .description('validate AsyncAPI file/url') .action(validate.command); +program + .command('convert ') + .description('convert AsyncAPI file/url from YAML to JSON and vice-versa. No compression is performed') + .option('-f, --format ', 'specify desired target format: json|yaml', /^(json|yaml)$/i) + .action((specFile, cmd) => { + convert.command(specFile, cmd.format) + }); program.parse(process.argv); diff --git a/convert.js b/convert.js new file mode 100644 index 0000000..46ba2d6 --- /dev/null +++ b/convert.js @@ -0,0 +1,20 @@ +#!/usr/bin/env node + +'use strict' + +const converter = require('./lib/converter.js'); +const formats = converter.formats; + +const command = async (specFilePath, targetFormat) => { + + if (targetFormat !== formats.YAML || targetFormat !== formats.JSON) { + console.log("throw `Invalid target format: ${targetFormat}`;"); + process.exit(1); + } + + const document = await converter.convert(specFilePath, targetFormat); + console.log(document); +}; + + +module.exports = { command }; diff --git a/lib/converter.js b/lib/converter.js new file mode 100644 index 0000000..ba1c75c --- /dev/null +++ b/lib/converter.js @@ -0,0 +1,41 @@ +'use strict'; +const parser = require('./parser'); +const YAML = require('js-yaml'); + + +const formats = Object.freeze({ + JSON: "json", + YAML: "yaml" +}); + +const convert = async (specFilePath, targetFormat) => { + + let content = await parser.getFileContent(specFilePath); + + content = content.toString('utf8'); + + if (targetFormat === formats.YAML) { + + let jsonObject = JSON.parse(content); + + return YAML.safeDump(jsonObject); + + } else if (targetFormat === formats.JSON) { + + const parsedContent = YAML.safeLoad(content); + + const bundledContent = await parser.bundle(parsedContent); + + const dereferencedContent = await parser.dereference(bundledContent); + + return JSON.stringify(dereferencedContent); + } + + throw `Invalid target format: ${targetFormat}`; +}; + +module.exports = { + convert: convert, + formats: formats +}; + diff --git a/lib/parser.js b/lib/parser.js index 274fccb..d011f51 100644 --- a/lib/parser.js +++ b/lib/parser.js @@ -32,7 +32,7 @@ async function dereference (json) { }); } -async function bundle (json) { +async function bundle(json) { return RefParser.bundle(json, { dereference: { circular: 'ignore' @@ -50,9 +50,8 @@ async function validate (json, schema) { } async function parse (filePath) { - let content, parsedContent, dereferencedJSON, bundledJSON, parsed; + let content, parsedContent, dereferencedJSON, bundledJSON, parsed; - try { if (typeof filePath === 'string') { content = await getFileContent(filePath); } else if (typeof filePath === 'object') { @@ -61,10 +60,7 @@ async function parse (filePath) { // console.error(`Could not find a valid asyncapi definition: ${filePath}`); throw `Could not find a valid asyncapi definition: ${filePath}`; } - } catch (e) { - // console.error('Can not load the content of the AsyncAPI specification file'); - throw e; - } + try { parsedContent = parseContent(content); @@ -102,4 +98,10 @@ async function parse (filePath) { return JSON.parse(JSON.stringify(parsed)); }; -module.exports = parse; +module.exports = { + parse: parse, + getFileContent: getFileContent, + dereference: dereference, + bundle, bundle +}; + diff --git a/lib/validator.js b/lib/validator.js index 023e076..32587a8 100644 --- a/lib/validator.js +++ b/lib/validator.js @@ -1,11 +1,10 @@ 'use strict'; -const parse = require('./parser'); +const parser = require('./parser'); const validate = async (specFile) => { - await parse(specFile); + await parser.parse(specFile); return true; - }; module.exports = {validate}; diff --git a/script.bash b/script.bash index 82161bf..fa3d9e7 100644 --- a/script.bash +++ b/script.bash @@ -1,2 +1,5 @@ #!/usr/bin/env bash -async-api-linter validate /Users/melianr/dev/asyncapi/async-api-linter/test/samples/asyncapi.yml +#async-api-linter validate /Users/melianr/dev/asyncapi/async-api-linter/test/samples/asyncapi.yml +#async-api-linter validate /Users/melianr/dev/asyncapi/async-api-linter/test/samples/asyncapi.json + +async-api-linter convert /Users/melianr/dev/asyncapi/async-api-linter/test/samples/asyncapi.yml diff --git a/test/samples/asyncapi.json b/test/samples/asyncapi.json new file mode 100644 index 0000000..6354088 --- /dev/null +++ b/test/samples/asyncapi.json @@ -0,0 +1,117 @@ +{ + "asyncapid": "1.2.0", + "info": { + "title": "Streetlights API", + "version": "1.0.0", + "description": "The Smartylighting Streetlights API allows you\nto remotely manage the city lights.\n", + "license": { + "name": "Apache 2.0", + "url": "https://www.apache.org/licenses/LICENSE-2.0" + } + }, + "baseTopic": "smartylighting.streetlights.1.0", + "servers": [ + { + "url": "test.mosquitto.org", + "scheme": "mqtt", + "description": "Test broker", + "variables": { + "port": { + "description": "Secure connection (TLS) is available through port 8883.", + "default": "1883", + "enum": [ + "1883", + "8883" + ] + } + } + } + ], + "topics": { + "event.lighting.measured": { + "x-service-name": "streetlights", + "publish": { + "x-operation-id": "lightMeasuredPublish", + "summary": "Inform about environmental lighting conditions for a particular streetlight.", + "payload": { + "type": "object", + "properties": { + "lumens": { + "type": "integer", + "minimum": 0, + "description": "Light intensity measured in lumens." + }, + "sentAt": { + "type": "string", + "format": "date-time", + "description": "Date and time when the message was sent." + } + } + } + }, + "subscribe": { + "x-operation-id": "lightMeasuredSubscribe", + "summary": "Inform about environmental lighting conditions for a particular streetlight.", + "payload": { + "type": "object", + "properties": { + "lumens": { + "type": "integer", + "minimum": 0, + "description": "Light intensity measured in lumens." + }, + "sentAt": { + "type": "string", + "format": "date-time", + "description": "Date and time when the message was sent." + } + } + } + } + } + }, + "components": { + "messages": { + "lightMeasured": { + "summary": "Inform about environmental lighting conditions for a particular streetlight.", + "payload": { + "type": "object", + "properties": { + "lumens": { + "type": "integer", + "minimum": 0, + "description": "Light intensity measured in lumens." + }, + "sentAt": { + "type": "string", + "format": "date-time", + "description": "Date and time when the message was sent." + } + } + } + } + }, + "schemas": { + "lightMeasuredPayload": { + "type": "object", + "properties": { + "lumens": { + "type": "integer", + "minimum": 0, + "description": "Light intensity measured in lumens." + }, + "sentAt": { + "type": "string", + "format": "date-time", + "description": "Date and time when the message was sent." + } + } + }, + "sentAt": { + "type": "string", + "format": "date-time", + "description": "Date and time when the message was sent." + } + } + } +} diff --git a/test/samples/asyncapi.yml b/test/samples/asyncapi.yml index 2c8db83..c82f09e 100644 --- a/test/samples/asyncapi.yml +++ b/test/samples/asyncapi.yml @@ -1,4 +1,4 @@ -asyncapi: '1.2.0' +asyncapid: '1.2.0' info: title: Streetlights API version: '1.0.0' @@ -29,14 +29,6 @@ topics: subscribe: x-operation-id: lightMeasuredSubscribe $ref: '#/components/messages/lightMeasured' - event.lighting.traced: - x-service-name: streetlights - publish: - x-operation-id: lightTracedPublish - $ref: '#/components/messages/lightMeasured' - subscribe: - x-operation-id: lightTracedSubscribe - $ref: '#/components/messages/lightMeasured' components: messages: lightMeasured: diff --git a/validate.js b/validate.js index aaac2a2..680046b 100644 --- a/validate.js +++ b/validate.js @@ -7,11 +7,11 @@ const validator = require('./lib/validator.js'); const command = async (specFile, cmd) => { try { await validator.validate(specFile); - // console.log("valid"); + console.log("Valid AsyncAPI document"); process.exit(0); }catch (e) { - // console.error("NOT valid"); - // console.error(e.message); + console.log("Non valid AsyncAPI document"); + console.error(e.message); process.exit(1); }