diff --git a/.gitignore b/.gitignore index 5f8d342..fa47056 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ node_modules .vscode-test/ *.vsix .DS_Store +package-lock.json \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index aae7fcf..cb75292 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,17 +10,18 @@ "license": "Apache-2.0", "dependencies": { "@asyncapi/parser": "^3.0.7", + "@types/markdown-it": "^13.0.7", "ejs": "^3.1.9", + "js-yaml": "^4.1.0", "markdown-it": "^14.0.0", "mermaid": "^10.8.0", - "@types/markdown-it": "^13.0.7" + "yaml": "^2.4.2" }, "devDependencies": { "@asyncapi/react-component": "^1.2.7", "@types/ejs": "^3.1.5", "@types/glob": "^7.2.0", - "@types/js-yaml": "^4.0.5", - "@types/markdown-it": "^13.0.7", + "@types/js-yaml": "^4.0.9", "@types/mocha": "^9.1.1", "@types/node": "14.x", "@types/vscode": "^1.66.0", @@ -733,9 +734,9 @@ } }, "node_modules/@types/js-yaml": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.5.tgz", - "integrity": "sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==", + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz", + "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==", "dev": true }, "node_modules/@types/json-schema": { @@ -743,18 +744,15 @@ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==" }, - "node_modules/@types/linkify-it": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.5.tgz", - "integrity": "sha512-yg6E+u0/+Zjva+buc3EIb+29XEg4wltq7cSmd4Uc2EE/1nUVmxyzpX6gUXD0V8jIrG0r7YeOGVIbYRkxeooCtw==", - "dev": true + "integrity": "sha512-yg6E+u0/+Zjva+buc3EIb+29XEg4wltq7cSmd4Uc2EE/1nUVmxyzpX6gUXD0V8jIrG0r7YeOGVIbYRkxeooCtw==" }, "node_modules/@types/markdown-it": { "version": "13.0.7", "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-13.0.7.tgz", "integrity": "sha512-U/CBi2YUUcTHBt5tjO2r5QV/x0Po6nsYwQU4Y04fBS6vfoImaiZ6f8bi3CjTCxBPQSO1LMyUqkByzi8AidyxfA==", - "dev": true, "dependencies": { "@types/linkify-it": "*", "@types/mdurl": "*" @@ -771,8 +769,7 @@ "node_modules/@types/mdurl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.5.tgz", - "integrity": "sha512-6L6VymKTzYSrEf4Nev4Xa1LCHKrlTlYCBMTlQKFuddo1CvQcE52I0mwfOJayueUC7MJuXOeHTcIU683lzd0cUA==", - "dev": true + "integrity": "sha512-6L6VymKTzYSrEf4Nev4Xa1LCHKrlTlYCBMTlQKFuddo1CvQcE52I0mwfOJayueUC7MJuXOeHTcIU683lzd0cUA==" }, "node_modules/@types/minimatch": { "version": "5.1.2", @@ -2075,7 +2072,6 @@ "node": ">=12" } }, - "node_modules/d3-brush": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", @@ -2091,7 +2087,6 @@ "node": ">=12" } }, - "node_modules/d3-chord": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", @@ -2111,7 +2106,6 @@ "node": ">=12" } }, - "node_modules/d3-contour": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz", @@ -2119,7 +2113,6 @@ "dependencies": { "d3-array": "^3.2.0" }, - "engines": { "node": ">=12" } @@ -2549,14 +2542,6 @@ "robust-predicates": "^3.0.2" } }, - "node_modules/delaunator": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz", - "integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==", - "dependencies": { - "robust-predicates": "^3.0.2" - } - }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -2582,14 +2567,6 @@ "node": ">=6" } }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "engines": { - "node": ">=6" - } - }, "node_modules/diff": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", @@ -2672,20 +2649,6 @@ "node": ">=0.10.0" } }, - "node_modules/ejs": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz", - "integrity": "sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==", - "dependencies": { - "jake": "^10.8.5" - }, - "bin": { - "ejs": "bin/cli.js" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/electron-to-chromium": { "version": "1.4.283", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.283.tgz", @@ -2697,11 +2660,6 @@ "resolved": "https://registry.npmjs.org/elkjs/-/elkjs-0.9.2.tgz", "integrity": "sha512-2Y/RaA1pdgSHpY0YG4TYuYCD2wh97CRvu22eLG3Kz0pgQ/6KbIFTxsTnDc4MH/6hFlg2L/9qXrDMG0nMjP63iw==" }, - "node_modules/elkjs": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/elkjs/-/elkjs-0.9.2.tgz", - "integrity": "sha512-2Y/RaA1pdgSHpY0YG4TYuYCD2wh97CRvu22eLG3Kz0pgQ/6KbIFTxsTnDc4MH/6hFlg2L/9qXrDMG0nMjP63iw==" - }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -3407,20 +3365,6 @@ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/fstream": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", @@ -4938,7 +4882,6 @@ "micromark-util-symbol": "^1.0.0" } }, - "node_modules/micromark-util-decode-string": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-1.1.0.tgz", @@ -4959,7 +4902,6 @@ "micromark-util-decode-numeric-character-reference": "^1.0.0", "micromark-util-symbol": "^1.0.0" } - }, "node_modules/micromark-util-encode": { "version": "1.1.0", @@ -4990,7 +4932,6 @@ "url": "https://opencollective.com/unified" } ] - }, "node_modules/micromark-util-normalize-identifier": { "version": "1.1.0", @@ -5083,7 +5024,6 @@ "url": "https://opencollective.com/unified" } ] - }, "node_modules/micromark-util-types": { "version": "1.1.0", @@ -7235,6 +7175,17 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, + "node_modules/yaml": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.2.tgz", + "integrity": "sha512-B3VqDZ+JAg1nZpaEmWtTXUlBneoGx6CPM9b0TENK6aoSu5t73dItudwdgmi6tHlIZZId4dZ9skcAQ2UbcyAeVA==", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", diff --git a/package.json b/package.json index b19a03d..964f24c 100644 --- a/package.json +++ b/package.json @@ -127,8 +127,6 @@ "@asyncapi/react-component": "^1.2.7", "@types/ejs": "^3.1.5", "@types/glob": "^7.2.0", - "@types/js-yaml": "^4.0.5", - "@types/markdown-it": "^13.0.7", "@types/mocha": "^9.1.1", "@types/node": "14.x", "@types/vscode": "^1.66.0", @@ -148,9 +146,10 @@ }, "dependencies": { "@asyncapi/parser": "^3.0.7", + "@types/markdown-it": "^13.0.7", "ejs": "^3.1.9", "markdown-it": "^14.0.0", - "mermaid": "^10.8.0" - "@types/markdown-it": "^13.0.7", + "mermaid": "^10.8.0", + "yaml": "^2.4.2" } } diff --git a/src/Asyncapi.ts b/src/Asyncapi.ts index dd654a1..8252eed 100644 --- a/src/Asyncapi.ts +++ b/src/Asyncapi.ts @@ -4,6 +4,8 @@ import * as ejs from 'ejs'; import * as path from 'path'; import * as Markdownit from 'markdown-it'; import { sample } from 'openapi-sampler'; +import { description, hasDescription } from '@asyncapi/parser/esm/old-api/mixins'; +import { bindings } from '@asyncapi/parser/esm/models/v3/mixins'; const md = Markdownit('commonmark'); @@ -22,7 +24,6 @@ const jsonSchemaTypes: string[] = [ 'null', ]; -const RESTRICTED_ANY = 'restricted any'; const NEVER = 'never'; const UNKNOWN = 'unknown'; const ANY = 'any'; @@ -318,7 +319,7 @@ class SchemaHelper { return; } - for (const [prop, array] of Object.entries(dependencies)) { + for (const [prop, array] of Object.entries(dependencies || {})) { if (Array.isArray(array) && array.includes(propertyName)) { dependentRequired.push(prop); } @@ -333,7 +334,7 @@ class SchemaHelper { } const records:any = {}; - for (const [prop, propSchema] of Object.entries(dependencies)) { + for (const [prop, propSchema] of Object.entries(dependencies || {})) { if (typeof propSchema === 'object' && !Array.isArray(propSchema)) { records[String(prop)] = propSchema; } @@ -344,7 +345,7 @@ class SchemaHelper { const json:object = { type: 'object', - properties: Object.entries(records).reduce( + properties: Object.entries(records || {}).reduce( (obj:any, [propertyName, propertySchema]:any[]) => { obj[String(propertyName)] = Object.assign({}, propertySchema.json()); return obj; @@ -413,7 +414,7 @@ class SchemaHelper { } return { type: 'object', - properties: Object.entries(value).reduce((obj:any, [k, v]) => { + properties: Object.entries(value || {}).reduce((obj:any, [k, v]) => { obj[String(k)] = this.jsonFieldToSchema(v); return obj; }, {}), @@ -608,12 +609,446 @@ class MessageHelper { } } -export default async function asyncapiMarkdown(asyncapi:AsyncAPIDocumentInterface, context: vscode.ExtensionContext) { +function addServers(asyncapi: any){ + return { + isEmpty: ()=> (asyncapi.servers)? false : true, + all: ()=>{ + return Object.entries(asyncapi.servers || {}).map((server: any)=>{ + return{ + id: ()=> server[0], + url: ()=> (server[1] && server[1].host)? server[1].host : "", + protocol: ()=> (server[1] && server[1].protocol)? server[1].protocol : "", + protocolVersion: ()=> "", + hasDescription: ()=> (server[1] && server[1].description)? true : false, + description: ()=> (server[1])? server[1].description : "", + variables: ()=> (server[1] && server[1].variables)? {all: ()=> Object.entries(server[1].variables || {}).map((entry: any)=>{ + return { + id: ()=> (entry[0])? entry[0] : "", + description: ()=> (entry[1] && entry[1].description)? entry[1].description : "", + hasDefaultValue: ()=> (entry[1] && entry[1].default)? true : false, + defaultValue: ()=> (entry[1] && entry[1].default)? entry[1].default : "", + hasAllowedValues: ()=> (entry[1] && entry[1].enum)? true : false, + allowedValues: ()=> (entry[1] && entry[1].enum)? entry[1].enum : [] + + }; + }) }: "", + security: ()=> { + return server[1].security?.map( (sec: any)=>{ + return { + all: ()=> Object.entries(sec || {}).map((security: any)=>{ + return { + scheme: ()=>{ + return { + type: ()=> (asyncapi.components && asyncapi.components.securitySchemes && asyncapi.components.securitySchemes[security[1].split('/').pop()])? asyncapi.components.securitySchemes[security[1].split('/').pop()].type : "", + hasDescription: ()=> (asyncapi.components && asyncapi.components.securitySchemes && asyncapi.components.securitySchemes[security[1].split('/').pop()] && asyncapi.components.securitySchemes[security[1].split('/').pop()].description )? true : false, + description: ()=> asyncapi.components.securitySchemes[security[1].split('/').pop()].description, + name: ()=> asyncapi.components.securitySchemes[security[1].split('/').pop()].name, + in: ()=> asyncapi.components.securitySchemes[security[1].split('/').pop()].in, + bearerFormat: ()=> asyncapi.components.securitySchemes[security[1].split('/').pop()].bearerFormat, + openIdConnectUrl: ()=> asyncapi.components.securitySchemes[security[1].split('/').pop()].openIdConnectUrl, + scheme: ()=> asyncapi.components.securitySchemes[security[1].split('/').pop()].scheme, + flows: ()=> { + if(!asyncapi.components.securitySchemes[security[1].split('/').pop()].flows) {return;} + return { + authorizationCode: ()=> { return { + authorizationUrl: ()=> asyncapi.components.securitySchemes[security[1].split('/').pop()].flows?.authorizationCode.authorizationUrl, + refreshUrl: ()=> asyncapi.components.securitySchemes[security[1].split('/').pop()].flows?.authorizationCode?.refreshUrl, + tokenUrl: ()=> asyncapi.components.securitySchemes[security[1].split('/').pop()].flows?.authorizationCode?.tokenUrl, + scopes: ()=> asyncapi.components.securitySchemes[security[1].split('/').pop()].flows?.authorizationCode?.availableScopes || [] + };}, + clientCredentials: ()=> { return { + authorizationUrl: ()=> asyncapi.components.securitySchemes[security[1].split('/').pop()]?.flows?.clientCredentials?.authorizationUrl, + refreshUrl: ()=> asyncapi.components.securitySchemes[security[1].split('/').pop()]?.flows?.clientCredentials?.refreshUrl, + tokenUrl: ()=> asyncapi.components.securitySchemes[security[1].split('/').pop()]?.flows?.clientCredentials?.tokenUrl, + scopes: ()=> asyncapi.components.securitySchemes[security[1].split('/').pop()]?.flows?.clientCredentials?.availableScopes || [] + };}, + implicit: ()=> { return { + authorizationUrl: ()=> asyncapi.components.securitySchemes[security[1].split('/').pop()]?.flows?.implicit?.authorizationUrl, + refreshUrl: ()=> asyncapi.components.securitySchemes[security[1].split('/').pop()]?.flows?.implicit?.refreshUrl, + tokenUrl: ()=> asyncapi.components.securitySchemes[security[1].split('/').pop()]?.flows?.implicit?.tokenUrl, + scopes: ()=> asyncapi.components.securitySchemes[security[1].split('/').pop()]?.flows?.implicit?.availableScopes || [] + };}, + password: ()=> { return { + authorizationUrl: ()=> asyncapi.components.securitySchemes[security[1].split('/').pop()]?.flows?.password?.authorizationUrl, + refreshUrl: ()=> asyncapi.components.securitySchemes[security[1].split('/').pop()]?.flows?.password?.refreshUrl, + tokenUrl: ()=> asyncapi.components.securitySchemes[security[1].split('/').pop()]?.flows?.password?.tokenUrl, + scopes: ()=> asyncapi.components.securitySchemes[security[1].split('/').pop()]?.flows?.password?.availableScopes || [] + };} + }; + } + }; + }, + scopes: ()=> (asyncapi.components && asyncapi.components.securitySchemes && asyncapi.components.securitySchemes[security[1].split('/').pop()])? asyncapi.components.securitySchemes[security[1].split('/').pop()].scopes || [] : "" + }; + }) + }; + }); + }, + extensions: ()=> { + return { + all: ()=> Object.entries(server[1].extensions || {}).map((extension: any)=>{ + return { + id: ()=> extension[1].id, + value: ()=> extension[1].value + }; + }) + }; + }, + bindings: ()=>{ + return { + isEmpty: ()=> (server[1].bindings)? false : true, + all: ()=> { + return Object.entries(server[1].bindings || {}).map((binding : any)=>{ + return { + protocol: ()=> (binding[1] && binding[1].protocol)? binding[1].protocol : "", + json: ()=> (binding[1] && binding[1].json)? binding[1].json : "", + type: ()=> (binding[1] && binding[1].type)? binding[1].type : "" + }; + }); + } + }; + }, + tags: ()=> { + return { + isEmpty: ()=> (server[1].tags)? false : true, + all: ()=> Object.entries(server[1].tags || {}).map((tag: any)=>{ + return { + name: ()=> (tag[1] && tag[1].name)? tag[1].name : "", + description: ()=> (tag[1] && tag[1].description)? tag[1].description : "", + externalDocs: ()=> (tag[1] && tag[1].externalDocs)? { + url: ()=> tag[1].externalDocs.url, + description: ()=> tag[1].externalDocs.description + } : "" + + }; + }) + }; + } + }; + }); + } + }; +} + +export default async function asyncapiMarkdown(asyncapi: any, context: vscode.ExtensionContext) { + const templatePath = path.join(context.extensionPath,'dist', 'components','Asyncapi.ejs'); + if(!asyncapi.isAsyncapiParser){ + return await ejs.renderFile(templatePath, { + info: { + title: (asyncapi.info && asyncapi.info.title)? asyncapi.info.title : "", + version: (asyncapi.info && asyncapi.info.version)? asyncapi.info.version : "", + defaultContentType: (asyncapi.defaultContentType)? asyncapi.defaultContentType : "", + specId: asyncapi.info && (asyncapi.info.specId)? asyncapi.info.specId : "", + termsOfService: (asyncapi.info && asyncapi.info.termsOfService)? asyncapi.info.termsOfService : "", + license: (asyncapi.info && asyncapi.info.license)? {url: ()=> asyncapi.info.license.url, name: ()=> asyncapi.info.license.name} : null, + contact: (asyncapi.info && asyncapi.info.contact)? {name: ()=> asyncapi.info.contact.name, url: ()=> asyncapi.info.contact.url, email: asyncapi.info.contact.email} : null, + externalDocs: (asyncapi.info && asyncapi.info.externalDocs)? {url: ()=> asyncapi.info.externalDocs.url(), description: ()=> asyncapi.info.externalDocs.description()} : null, + hasDescription: (asyncapi.info && asyncapi.info.description)? true: false, + description:(asyncapi.info)? md.render(asyncapi.info.description || "") : "", + tags: { + isEmpty: ()=> (asyncapi.info.tags)? false : true, + all: ()=> Object.entries(asyncapi.info.tags || {}).map((tag: any)=>{ + return { + name: ()=> (tag[1] && tag[1].name)? tag[1].name : "", + description: ()=> (tag[1] && tag[1].description)? tag[1].description : "", + externalDocs: ()=> (tag[1] && tag[1].externalDocs)? { + url: ()=> tag[1].externalDocs.url, + description: ()=> tag[1].externalDocs?.description, + hasDescription: ()=> tag[1].externalDocs?.description? true : false + } : "" + }; + }) + } + }, + servers:{ + servers: addServers(asyncapi), + schemaHelper: SchemaHelper, + serverHelper: ServerHelper, + md + }, + operations:{ + channels: { + isEmpty: ()=> (asyncapi.channels)? false : true, + all: ()=> Object.entries(asyncapi.channels || {}).map((channel: any)=>{ + return { + servers: ()=> addServers(asyncapi), + operations: ()=> { + return { + all: ()=> Object.entries(asyncapi.operations || {}).filter((operation: any)=> operation[1].channel?.$ref.split('channels/').pop().replaceAll('~1','/') === channel[0]).map((operation: any)=>{ + return { + operationId: ()=> (operation[0])? operation[0] : "", + isSend: ()=> (operation[1] && operation[1].action === 'send')? true : false, + isReceive: ()=> (operation[1] && operation[1].action === 'receive')? true : false, + reply: ()=> (operation[1] && operation[1].reply)? operation[1].reply : false, + summary: ()=> (operation[1] && operation[1].summary)? operation[1].summary : "", + hasDescription: ()=> (operation[1] && operation[1].description)? true : false, + description: ()=> (operation[1] && operation[1].description)? operation[1].description : "", + externalDocs: ()=> (operation[1] && operation[1].externalDocs)? { + url: ()=> operation[1].externalDocs.url, + description: ()=> operation[1].externalDocs.description, + hasDescription: ()=> operation[1].externalDocs.description? true : false + } : "", + tags: ()=> { + return { + isEmpty: ()=> (operation[1].tags)? false : true, + all: ()=> Object.entries(operation[1].tags || {}).map((tag: any)=>{ + return { + name: ()=> (tag[1] && tag[1].name)? tag[1].name : "", + description: ()=> (tag[1] && tag[1].description)? tag[1].description : "", + externalDocs: ()=> (tag[1] && tag[1].externalDocs)? { + url: ()=> tag[1].externalDocs?.url, + description: ()=> tag[1].externalDocs?.description, + hasDescription: ()=> tag[1].externalDocs?.description? true : false + } : "" + + }; + }) + }; + }, + extensions: ()=> { + return { + all: ()=> Object(operation[1].extensions).map((extension: any)=>{ + return { + id: ()=> extension[1].id, + value: ()=> extension[1].value + }; + }) + }; + }, + bindings: ()=>{ + return { + isEmpty: ()=> (operation[1].bindings)? false : true, + all: ()=> { + return Object.entries(operation[1].bindings || {}).map((binding : any)=>{ + return { + protocol: ()=> (binding[1] && binding[1].protocol)? binding[1].protocol : "", + json: ()=> (binding[1] && binding[1].json)? binding[1].json : "", + type: ()=> (binding[1] && binding[1].type)? binding[1].type : "" + }; + }); + } + }; + }, + security: ()=> { + return operation[1].security?.map( (sec: any)=>{ + return { + all: ()=> Object.entries(sec || {}).map((security: any)=>{ + return { + scheme: ()=>{ + return { + type: ()=> (asyncapi.components && asyncapi.components.securitySchemes && asyncapi.components.securitySchemes[security[1].split('/').pop()])? asyncapi.components.securitySchemes[security[1].split('/').pop()].type : "", + hasDescription: ()=> (asyncapi.components && asyncapi.components.securitySchemes && asyncapi.components.securitySchemes[security[1].split('/').pop()] && asyncapi.components.securitySchemes[security[1].split('/').pop()].description )? true : false, + description: ()=> asyncapi.components.securitySchemes[security[1].split('/').pop()].description, + name: ()=> asyncapi.components.securitySchemes[security[1].split('/').pop()].name, + in: ()=> asyncapi.components.securitySchemes[security[1].split('/').pop()].in, + bearerFormat: ()=> asyncapi.components.securitySchemes[security[1].split('/').pop()].bearerFormat, + openIdConnectUrl: ()=> asyncapi.components.securitySchemes[security[1].split('/').pop()].openIdConnectUrl, + scheme: ()=> asyncapi.components.securitySchemes[security[1].split('/').pop()].scheme, + flows: ()=> { + if(!asyncapi.components.securitySchemes[security[1].split('/').pop()].flows) {return;} + return { + authorizationCode: ()=> { return { + authorizationUrl: ()=> asyncapi.components.securitySchemes[security[1].split('/').pop()].flows?.authorizationCode.authorizationUrl, + refreshUrl: ()=> asyncapi.components.securitySchemes[security[1].split('/').pop()].flows?.authorizationCode?.refreshUrl, + tokenUrl: ()=> asyncapi.components.securitySchemes[security[1].split('/').pop()].flows?.authorizationCode?.tokenUrl, + scopes: ()=> asyncapi.components.securitySchemes[security[1].split('/').pop()].flows?.authorizationCode?.availableScopes || [] + };}, + clientCredentials: ()=> { return { + authorizationUrl: ()=> asyncapi.components.securitySchemes[security[1].split('/').pop()]?.flows?.clientCredentials?.authorizationUrl, + refreshUrl: ()=> asyncapi.components.securitySchemes[security[1].split('/').pop()]?.flows?.clientCredentials?.refreshUrl, + tokenUrl: ()=> asyncapi.components.securitySchemes[security[1].split('/').pop()]?.flows?.clientCredentials?.tokenUrl, + scopes: ()=> asyncapi.components.securitySchemes[security[1].split('/').pop()]?.flows?.clientCredentials?.availableScopes || [] + };}, + implicit: ()=> { return { + authorizationUrl: ()=> asyncapi.components.securitySchemes[security[1].split('/').pop()]?.flows?.implicit?.authorizationUrl, + refreshUrl: ()=> asyncapi.components.securitySchemes[security[1].split('/').pop()]?.flows?.implicit?.refreshUrl, + tokenUrl: ()=> asyncapi.components.securitySchemes[security[1].split('/').pop()]?.flows?.implicit?.tokenUrl, + scopes: ()=> asyncapi.components.securitySchemes[security[1].split('/').pop()]?.flows?.implicit?.availableScopes || [] + };}, + password: ()=> { return { + authorizationUrl: ()=> asyncapi.components.securitySchemes[security[1].split('/').pop()]?.flows?.password?.authorizationUrl, + refreshUrl: ()=> asyncapi.components.securitySchemes[security[1].split('/').pop()]?.flows?.password?.refreshUrl, + tokenUrl: ()=> asyncapi.components.securitySchemes[security[1].split('/').pop()]?.flows?.password?.tokenUrl, + scopes: ()=> asyncapi.components.securitySchemes[security[1].split('/').pop()]?.flows?.password?.availableScopes || [] + };} + }; + } + }; + }, + scopes: ()=> (asyncapi.components && asyncapi.components.securitySchemes && asyncapi.components.securitySchemes[security[1].split('/').pop()])? asyncapi.components.securitySchemes[security[1].split('/').pop()].scopes || [] : "" + }; + }) + }; + }); + }, + messages: ()=>{ + return { + all: ()=>{ + let tmp: any = Object.values(operation[1].messages); + if(tmp[0]?.$ref) { + return [{incorrect: true, refs: Object.values(operation[1].messages)}]; + } + return Object.entries(operation[1].messages|| {}).map((message: any)=>{ + return { + id: ()=> message[0], + title: ()=> message[1].title, + name: ()=> message[1].name, + hasDescription: ()=> (message[1].description)? true : false, + description: ()=> message[1].description, + contentType: ()=> message[1].contentType, + summary: ()=> message[1].summary, + correlationId: ()=> { + return { + location: ()=> message[1].correlationId?.location, + hasDescription: ()=> message[1].correlationId? true : false, + description: ()=> message[1].correlationId?.description + }; + }, + externalDocs: ()=> { + return { + url: ()=> message[1].externalDocs?.url, + description: ()=> message[1].externalDocs?.description + }; + }, + headers: ()=> { + return { + incorrect: true, + ...message[1].headers + }; + }, + payload: ()=> { + return { + incorrect: true, + ...message[1].payload + }; + }, + tags: ()=> { + return { + isEmpty: ()=> (operation[1].tags)? false : true, + all: ()=> Object.entries(operation[1].tags || {}).map((tag: any)=>{ + return { + name: ()=> (tag[1] && tag[1].name)? tag[1].name : "", + description: ()=> (tag[1] && tag[1].description)? tag[1].description : "", + externalDocs: ()=> (tag[1] && tag[1].externalDocs)? { + url: ()=> tag[1].externalDocs.url, + description: ()=> tag[1].externalDocs?.description, + hasDescription: ()=> tag[1].externalDocs?.description? true : false + } : "" + + }; + }) + }; + }, + extensions: ()=> { + return { + all: ()=> Object.entries(message[1].extensions || {}).map((extension: any)=>{ + return { + id: ()=> extension[1].id, + value: ()=> extension[1].value + }; + }) + }; + }, + bindings: ()=>{ + return { + isEmpty: ()=> (message[1].bindings)? false : true, + all: ()=> { + return Object.entries(message[1].bindings || {}).map((binding : any)=>{ + return { + protocol: ()=> (binding[1] && binding[1].protocol)? binding[1].protocol : "", + json: ()=> (binding[1] && binding[1].json)? binding[1].json : "", + type: ()=> (binding[1] && binding[1].type)? binding[1].type : "" + }; + }); + } + }; + } + }; + }); + } + }; + } + }; + }), + + }; + }, + address: ()=> (channel[1])? channel[1].address : "", + hasDescription: ()=> (channel[1] && channel[1].description)? true : false, + description: ()=> (channel[1] && channel[1].description)? channel[1].description : "", + parameters: ()=> { + return { + all: ()=> Object.entries(channel[1].parameters || {}).map((parameter: any)=> { + return { + id: ()=> parameter[0], + schema: ()=> { + return { + json: ()=>{ + return{ + type: parameter[1].schema?.type, + title: parameter[1].schema?.title, + required: parameter[1].schema?.required + }; + } + }; + }, + description: ()=>parameter[1].description, + location: ()=>parameter[1].location + }; + }) + }; + }, + extensions: ()=> { + return { + all: ()=> Object.entries(channel[1].extensions || {}).map((extension: any)=>{ + return { + id: ()=> extension[1].id, + value: ()=> extension[1].value + }; + }) + }; + }, + bindings: ()=>{ + return { + isEmpty: ()=> (channel[1].bindings)? false : true, + all: ()=> { + return Object.entries(channel[1].bindings || {}).map((binding : any)=>{ + return { + protocol: ()=> (binding[1] && binding[1].protocol)? binding[1].protocol : "", + json: ()=> (binding[1] && binding[1].json)? binding[1].json : "", + type: ()=> (binding[1] && binding[1].type)? binding[1].type : "" + }; + }); + } + }; + } + + }; + }) + }, + isV3: (asyncapi.asyncapi)? asyncapi.asyncapi.split('.')[0] === '3' : true, + schemaHelper: SchemaHelper, + serverHelper: ServerHelper, + messageHelper: MessageHelper, + allServersLength: (asyncapi.servers)? Object.keys(asyncapi.servers).length : 0, + md + }, + path:{ + infoPath: path.join(context.extensionPath,'dist', 'components','Info.ejs'), + tagsPath: path.join(context.extensionPath,'dist', 'components','Tags.ejs'), + serversPath: path.join(context.extensionPath,'dist', 'components','Servers.ejs'), + securityPath: path.join(context.extensionPath,'dist', 'components','Security.ejs'), + bindingsPath: path.join(context.extensionPath,'dist', 'components','Bindings.ejs'), + extensionsPath: path.join(context.extensionPath,'dist', 'components','Extensions.ejs'), + schemaPath: path.join(context.extensionPath,'dist', 'components','Schema.ejs'), + operationsPath: path.join(context.extensionPath,'dist', 'components','Operations.ejs'), + messagePath: path.join(context.extensionPath,'dist', 'components','Message.ejs') + } + }); + }else{ const info = asyncapi.info(); - const templatePath = path.join(context.extensionPath,'dist', 'components','Asyncapi.ejs'); - return await ejs.renderFile(templatePath, { info: { title: info.title(), @@ -657,4 +1092,7 @@ export default async function asyncapiMarkdown(asyncapi:AsyncAPIDocumentInterfac messagePath: path.join(context.extensionPath,'dist', 'components','Message.ejs') } }); + } + + } \ No newline at end of file diff --git a/src/PreviewMarkdown.ts b/src/PreviewMarkdown.ts index 9ea5908..3e819d5 100644 --- a/src/PreviewMarkdown.ts +++ b/src/PreviewMarkdown.ts @@ -1,27 +1,70 @@ import * as vscode from 'vscode'; import * as path from 'path'; +import * as fs from 'fs'; import { isAsyncAPIFile } from './PreviewWebPanel'; +import Diagnostics from './Diagnostics'; import { Parser, fromFile, AsyncAPIDocumentInterface } from '@asyncapi/parser'; import Asyncapi from './Asyncapi'; import { ISpectralDiagnostic } from '@stoplight/spectral-core'; -import Diagnostics from './Diagnostics'; +import { parse } from 'yaml'; + +function delay(ms: number | undefined) { + return new Promise(resolve => setTimeout(resolve, ms)); +} + + function parsedAsyncapiPreview(){ + const editor: any = vscode.window.activeTextEditor; + if(!editor) {return;} + const document = editor.document; + const filePath: any = document?.fileName; + const fullPath = path.resolve(filePath); + const content = fs.readFileSync(fullPath, 'utf8'); + + let parsedData; + if (filePath.endsWith('.json')) { + parsedData = JSON.parse(content); + } else if (filePath.endsWith('.yaml') || filePath.endsWith('.yml')) { + parsedData = parse(content); + } else { + vscode.window.showInformationMessage('Unsupported file type.'); + return; + } + + return parsedData; +} const parser = new Parser(); -async function buildMarkdown(document:AsyncAPIDocumentInterface | undefined, diagnostics: ISpectralDiagnostic[], context: vscode.ExtensionContext){ +async function buildMarkdown(document: any, diagnostics: ISpectralDiagnostic[], context: vscode.ExtensionContext){ + let content = ''; if(document !== undefined){ - + vscode.window.onDidChangeActiveTextEditor(()=>null); + document.isAsyncapiParser = true; content = await Asyncapi(document, context); }else{ content = await Diagnostics(diagnostics, context); + vscode.window.onDidChangeActiveTextEditor(async()=>{ + + let parsedData: any = parsedAsyncapiPreview(); + if(parsedData){ + parsedData.isAsyncapiParser = false; + content += await Asyncapi(parsedData, context); + } + }); + let parsedData: any = parsedAsyncapiPreview(); + if(parsedData){ + parsedData.isAsyncapiParser = false; + content += await Asyncapi(parsedData, context); + } } return content; + } export function previewMarkdown(context: vscode.ExtensionContext) { @@ -56,10 +99,10 @@ export async function openAsyncAPIMarkdown(context: vscode.ExtensionContext, uri localResourceRoots, }); - const { document, diagnostics } = await fromFile(parser, uri.fsPath).parse(); - console.log(diagnostics); + const { document, diagnostics } = await fromFile(parser, uri.fsPath).parse(); let result = await buildMarkdown(document, diagnostics, context); + panel.title = path.basename(uri.fsPath); panel.webview.html = getWebviewContent(context, panel.webview, uri, result); @@ -89,7 +132,7 @@ function getWebviewContent(context: vscode.ExtensionContext, webview: vscode.Web const mermaidJs = webview.asWebviewUri( vscode.Uri.joinPath(context.extensionUri, 'dist/node_modules/mermaid/dist/mermaid.min.js') ); - const globalsCss = webview.asWebviewUri( + const globalsCSS = webview.asWebviewUri( vscode.Uri.joinPath(context.extensionUri, 'dist/globals.css') ); @@ -97,7 +140,7 @@ function getWebviewContent(context: vscode.ExtensionContext, webview: vscode.Web
- + diff --git a/src/components/Diagnostics.ejs b/src/components/Diagnostics.ejs index 8f47733..4494827 100644 --- a/src/components/Diagnostics.ejs +++ b/src/components/Diagnostics.ejs @@ -9,7 +9,8 @@ <% }else if(diagnostic.severity == 3) { %> - <% } %> + <% } %> +<%= diagnostic.path %>
diff --git a/src/components/Info.ejs b/src/components/Info.ejs index a4d499c..9ac28ce 100644 --- a/src/components/Info.ejs +++ b/src/components/Info.ejs @@ -1,7 +1,5 @@Examples of headers@@ -76,11 +76,34 @@ <% } %> <% } %> - <% } %> + <% }else if(headers && headers.incorrect) { %> + <% function renderHeaderObject(obj, indent) { %> + <% for (let key in obj) { %> + <% if (key != 'incorrect') { %> + <% if (typeof obj[key] === 'object' && obj[key] !== null) { %> +
Examples of payload@@ -100,10 +123,40 @@ <% } %> - <% } %> + <% }else if(payload && payload.incorrect) { %> + <% function renderPayloadObject(obj, indent) { %> + <% for (let key in obj) { %> + <% if(key != 'incorrect') { %> + <% if (typeof obj[key] === 'object' && obj[key] !== null) { %> +