diff --git a/cli.js b/cli.js index 2615f721..f6f22a8f 100755 --- a/cli.js +++ b/cli.js @@ -37,9 +37,16 @@ if (!version) { try { const asyncapi = fs.readFileSync(asyncapiFile, 'utf-8'); - console.log(converter.convert(asyncapi, version, { + let converted = converter.convert(asyncapi, version, { id: program.id, - })); + }); + + // JSON case + if (typeof converted === 'object') { + converted = JSON.stringify(converted, undefined, 2); + } + + console.log(converted); } catch (e) { showErrorAndExit(e); } diff --git a/lib/helpers.js b/lib/helpers.js index 39e7028a..e4cb040e 100644 --- a/lib/helpers.js +++ b/lib/helpers.js @@ -3,16 +3,34 @@ const yaml = require('js-yaml'); const helpers = module.exports; helpers.serialize = (text) => { + if (typeof text === 'object') { + return { + isYAML: false, + parsed: text, + }; + } + try { + const maybeJSON = JSON.parse(text); + if (typeof maybeJSON === 'object') { + return { + isYAML: false, + parsed: maybeJSON, + }; + } + + // if `maybeJSON` is object, then we have 100% sure that we operate on JSON, + // but if it's `string` then we have option that it can be YAML but it doesn't have to be return { isYAML: true, parsed: yaml.safeLoad(text) }; } catch (e) { try { + // try to parse again YAML, because the text itself may not have a JSON representation and cannot be represented as a JSON object/string return { - isYAML: false, - parsed: JSON.parse(text) + isYAML: true, + parsed: yaml.safeLoad(text) }; } catch (err) { throw new Error('AsyncAPI document must be a valid JSON or YAML document.'); diff --git a/test/index.js b/test/index.js index 62e9902c..62aaa456 100644 --- a/test/index.js +++ b/test/index.js @@ -2,6 +2,7 @@ const assert = require('assert'); const fs = require('fs'); const path = require("path"); const { convert } = require('../lib'); +const { serialize } = require('../lib/helpers'); describe('#convert', () => { it('should not convert to lowest version', () => { @@ -193,6 +194,48 @@ describe('#convert', () => { const result = convert(input, '2.3.0'); assertResults(output, result); }); + + it('should convert from 2.0.0 to 2.1.0 (JSON case)', () => { + const input = fs.readFileSync(path.resolve(__dirname, 'input', '2.0.0', 'streetlights.json'), 'utf8'); + let output = fs.readFileSync(path.resolve(__dirname, 'output', '2.1.0', 'streetlights.json'), 'utf8'); + let result = convert(input, '2.1.0'); + + output = JSON.stringify(JSON.parse(output)); + result = JSON.stringify(JSON.parse(JSON.stringify(result))); + assert.strictEqual(output, result); + }); +}); + +describe('#serialize', () => { + it('should serialize JSON', () => { + const input = '{"foo": "bar"}'; + const output = serialize(input); + assert.strictEqual(output.isYAML, false); + assert.deepEqual(output.parsed, {foo: "bar"}); + }); + + it('should serialize YAML', () => { + const input = 'foo: bar'; + const output = serialize(input); + assert.strictEqual(output.isYAML, true); + assert.deepEqual(output.parsed, {foo: "bar"}); + }); + + it('should serialize YAML (with JSON syntax)', () => { + const input = '{foo: bar}'; + const output = serialize(input); + assert.strictEqual(output.isYAML, true); + assert.deepEqual(output.parsed, {foo: "bar"}); + }); + + it('should throw error', () => { + const input = '%{foo: bar}'; + try { + serialize(input); + } catch(e) { + assert.strictEqual(e.message, 'AsyncAPI document must be a valid JSON or YAML document.'); + } + }); }); /* @@ -205,4 +248,4 @@ function removeLineBreaks(str) { function assertResults(output, result){ assert.strictEqual(removeLineBreaks(output), removeLineBreaks(result)); -} \ No newline at end of file +} diff --git a/test/input/2.0.0/streetlights.json b/test/input/2.0.0/streetlights.json new file mode 100644 index 00000000..b226ee7a --- /dev/null +++ b/test/input/2.0.0/streetlights.json @@ -0,0 +1,172 @@ +{ + "asyncapi": "2.0.0", + "info": { + "title": "Streetlights API", + "version": "1.0.0", + "description": "The Smartylighting Streetlights API allows you to remotely manage the city lights.\n\n### Check out its awesome features:\n\n* Turn a specific streetlight on/off 🌃\n* Dim a specific streetlight 😎\n* Receive real-time information about environmental lighting conditions 📈\n", + "license": { + "name": "Apache 2.0", + "url": "https://www.apache.org/licenses/LICENSE-2.0" + } + }, + "servers": { + "default": { + "url": "api.streetlights.smartylighting.com:{port}", + "description": "Test broker", + "variables": { + "port": { + "description": "Secure connection (TLS) is available through port 8883.", + "default": "1883", + "enum": [ + "1883", + "8883" + ] + } + }, + "protocol": "mqtt", + "security": [ + { + "apiKey": [] + } + ] + } + }, + "components": { + "messages": { + "lightMeasured": { + "summary": "Inform about environmental lighting conditions for a particular streetlight.", + "payload": { + "$ref": "#/components/schemas/lightMeasuredPayload" + } + }, + "turnOnOff": { + "summary": "Command a particular streetlight to turn the lights on or off.", + "payload": { + "$ref": "#/components/schemas/turnOnOffPayload" + } + }, + "dimLight": { + "summary": "Command a particular streetlight to dim the lights.", + "payload": { + "$ref": "#/components/schemas/dimLightPayload" + } + } + }, + "schemas": { + "lightMeasuredPayload": { + "type": "object", + "properties": { + "lumens": { + "type": "integer", + "minimum": 0, + "description": "Light intensity measured in lumens." + }, + "sentAt": { + "$ref": "#/components/schemas/sentAt" + } + } + }, + "turnOnOffPayload": { + "type": "object", + "properties": { + "command": { + "type": "string", + "enum": [ + "on", + "off" + ], + "description": "Whether to turn on or off the light." + }, + "sentAt": { + "$ref": "#/components/schemas/sentAt" + } + } + }, + "dimLightPayload": { + "type": "object", + "properties": { + "percentage": { + "type": "integer", + "description": "Percentage to which the light should be dimmed to.", + "minimum": 0, + "maximum": 100 + }, + "sentAt": { + "$ref": "#/components/schemas/sentAt" + } + } + }, + "sentAt": { + "type": "string", + "format": "date-time", + "description": "Date and time when the message was sent." + } + }, + "securitySchemes": { + "apiKey": { + "type": "apiKey", + "in": "user", + "description": "Provide your API key as the user and leave the password empty." + } + }, + "parameters": { + "streetlightId": { + "name": "streetlightId", + "description": "The ID of the streetlight.", + "schema": { + "type": "string" + } + } + } + }, + "channels": { + "smartylighting/streetlights/1/0/event/{streetlightId}/lighting/measured": { + "parameters": { + "streetlightId": { + "$ref": "#/components/parameters/streetlightId" + } + }, + "publish": { + "message": { + "$ref": "#/components/messages/lightMeasured" + } + } + }, + "smartylighting/streetlights/1/0/action/{streetlightId}/turn/on": { + "parameters": { + "streetlightId": { + "$ref": "#/components/parameters/streetlightId" + } + }, + "subscribe": { + "message": { + "$ref": "#/components/messages/turnOnOff" + } + } + }, + "smartylighting/streetlights/1/0/action/{streetlightId}/turn/off": { + "parameters": { + "streetlightId": { + "$ref": "#/components/parameters/streetlightId" + } + }, + "subscribe": { + "message": { + "$ref": "#/components/messages/turnOnOff" + } + } + }, + "smartylighting/streetlights/1/0/action/{streetlightId}/dim": { + "parameters": { + "streetlightId": { + "$ref": "#/components/parameters/streetlightId" + } + }, + "subscribe": { + "message": { + "$ref": "#/components/messages/dimLight" + } + } + } + } +} \ No newline at end of file diff --git a/test/output/2.1.0/streetlights.json b/test/output/2.1.0/streetlights.json new file mode 100644 index 00000000..67516481 --- /dev/null +++ b/test/output/2.1.0/streetlights.json @@ -0,0 +1,172 @@ +{ + "asyncapi": "2.1.0", + "info": { + "title": "Streetlights API", + "version": "1.0.0", + "description": "The Smartylighting Streetlights API allows you to remotely manage the city lights.\n\n### Check out its awesome features:\n\n* Turn a specific streetlight on/off 🌃\n* Dim a specific streetlight 😎\n* Receive real-time information about environmental lighting conditions 📈\n", + "license": { + "name": "Apache 2.0", + "url": "https://www.apache.org/licenses/LICENSE-2.0" + } + }, + "servers": { + "default": { + "url": "api.streetlights.smartylighting.com:{port}", + "description": "Test broker", + "variables": { + "port": { + "description": "Secure connection (TLS) is available through port 8883.", + "default": "1883", + "enum": [ + "1883", + "8883" + ] + } + }, + "protocol": "mqtt", + "security": [ + { + "apiKey": [] + } + ] + } + }, + "components": { + "messages": { + "lightMeasured": { + "summary": "Inform about environmental lighting conditions for a particular streetlight.", + "payload": { + "$ref": "#/components/schemas/lightMeasuredPayload" + } + }, + "turnOnOff": { + "summary": "Command a particular streetlight to turn the lights on or off.", + "payload": { + "$ref": "#/components/schemas/turnOnOffPayload" + } + }, + "dimLight": { + "summary": "Command a particular streetlight to dim the lights.", + "payload": { + "$ref": "#/components/schemas/dimLightPayload" + } + } + }, + "schemas": { + "lightMeasuredPayload": { + "type": "object", + "properties": { + "lumens": { + "type": "integer", + "minimum": 0, + "description": "Light intensity measured in lumens." + }, + "sentAt": { + "$ref": "#/components/schemas/sentAt" + } + } + }, + "turnOnOffPayload": { + "type": "object", + "properties": { + "command": { + "type": "string", + "enum": [ + "on", + "off" + ], + "description": "Whether to turn on or off the light." + }, + "sentAt": { + "$ref": "#/components/schemas/sentAt" + } + } + }, + "dimLightPayload": { + "type": "object", + "properties": { + "percentage": { + "type": "integer", + "description": "Percentage to which the light should be dimmed to.", + "minimum": 0, + "maximum": 100 + }, + "sentAt": { + "$ref": "#/components/schemas/sentAt" + } + } + }, + "sentAt": { + "type": "string", + "format": "date-time", + "description": "Date and time when the message was sent." + } + }, + "securitySchemes": { + "apiKey": { + "type": "apiKey", + "in": "user", + "description": "Provide your API key as the user and leave the password empty." + } + }, + "parameters": { + "streetlightId": { + "name": "streetlightId", + "description": "The ID of the streetlight.", + "schema": { + "type": "string" + } + } + } + }, + "channels": { + "smartylighting/streetlights/1/0/event/{streetlightId}/lighting/measured": { + "parameters": { + "streetlightId": { + "$ref": "#/components/parameters/streetlightId" + } + }, + "publish": { + "message": { + "$ref": "#/components/messages/lightMeasured" + } + } + }, + "smartylighting/streetlights/1/0/action/{streetlightId}/turn/on": { + "parameters": { + "streetlightId": { + "$ref": "#/components/parameters/streetlightId" + } + }, + "subscribe": { + "message": { + "$ref": "#/components/messages/turnOnOff" + } + } + }, + "smartylighting/streetlights/1/0/action/{streetlightId}/turn/off": { + "parameters": { + "streetlightId": { + "$ref": "#/components/parameters/streetlightId" + } + }, + "subscribe": { + "message": { + "$ref": "#/components/messages/turnOnOff" + } + } + }, + "smartylighting/streetlights/1/0/action/{streetlightId}/dim": { + "parameters": { + "streetlightId": { + "$ref": "#/components/parameters/streetlightId" + } + }, + "subscribe": { + "message": { + "$ref": "#/components/messages/dimLight" + } + } + } + } +} \ No newline at end of file