From e93d2bcf492a689f8097363ab5236684916074a1 Mon Sep 17 00:00:00 2001 From: Joseph Avila Alvarez <46098690+josephinoo@users.noreply.github.com> Date: Wed, 21 Feb 2024 13:46:31 -0500 Subject: [PATCH] WIP --- package.json | 7 +- src/ConvertMD.ts | 206 +++++++++++++++++++++++++++++++++++++++++ src/PreviewWebPanel.ts | 40 ++++++-- src/extension.ts | 4 +- 4 files changed, 246 insertions(+), 11 deletions(-) create mode 100644 src/ConvertMD.ts diff --git a/package.json b/package.json index 59f9b57..c709acd 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,10 @@ { "command": "asyncapi.paste", "title": "AsyncAPI: Paste as Schema" + }, + { + "command": "asyncapi.md", + "title": "AsyncAPI: Convert to Markdown" } ], "snippets": [ @@ -121,6 +125,7 @@ "ts-loader": "^9.2.8", "typescript": "^4.6.4", "webpack": "^5.70.0", - "webpack-cli": "^4.9.2" + "webpack-cli": "^4.9.2", + "mermaid": "^10.8.0" } } diff --git a/src/ConvertMD.ts b/src/ConvertMD.ts new file mode 100644 index 0000000..3162c4b --- /dev/null +++ b/src/ConvertMD.ts @@ -0,0 +1,206 @@ +import * as fs from 'fs'; +import * as yaml from 'js-yaml'; + +interface AsyncAPIDocument { + asyncapi: string; + info: { + title: string; + version: string; + description: string; + }; + servers: { + [key: string]: { + host: string; + protocol: string; + description: string; + }; + }; + channels: { + [key: string]: { + address: string; + description: string; + messages: { + [key: string]: { + $ref: string; + }; + }; + parameters?: { + [key: string]: { + $ref: string; + }; + }; + }; + }; + operations: { + [key: string]: { + action: string; + channel: { + $ref: string; + }; + summary: string; + traits?: { + [key: string]: { + $ref: string; + }; + }; + messages?: { + [key: string]: { + $ref: string; + }; + }; + }; + }; + components?: { + messages?: { + [key: string]: { + name: string; + title: string; + summary: string; + contentType: string; + traits?: { + [key: string]: { + $ref: string; + }; + }; + payload?: { + $ref: string; + }; + }; + }; + schemas?: { + [key: string]: { + type: string; + properties: { + [key: string]: { + type: string; + minimum?: number; + maximum?: number; + description?: string; + enum?: string[]; + format?: string; + items?: { + type: string; + }; + $ref?: string; + }; + }; + description?: string; + }; + }; + securitySchemes?: { + [key: string]: { + type: string; + description: string; + }; + }; + parameters?: { + [key: string]: { + description: string; + schema: { + type: string; + format?: string; + minimum?: number; + maximum?: number; + }; + }; + }; + messageTraits?: { + [key: string]: { + headers: { + type: string; + properties: { + [key: string]: { + type: string; + minimum?: number; + maximum?: number; + }; + }; + }; + }; + }; + operationTraits?: { + [key: string]: { + bindings: { + kafka: { + clientId: { + type: string; + enum: string[]; + }; + }; + }; + }; + }; + }; +} +export function convertAsyncAPIToMermaid(asyncAPIFilePath:string): string { + const yamlFile = fs.readFileSync(asyncAPIFilePath, 'utf8'); + const asyncAPIDocument: AsyncAPIDocument = yaml.load(yamlFile) as AsyncAPIDocument; + let mermaidCode = `flowchart TD\n`; + + mermaidCode += ` subgraph "${asyncAPIDocument.info.title}"\n`; + + // Add Servers subgraph + mermaidCode += ` subgraph "Servers"\n`; + Object.entries(asyncAPIDocument.servers).forEach(([serverName, serverInfo]) => { + mermaidCode += ` ${serverName}["${serverName}"]\n`; + }); + mermaidCode += ` end\n`; + + // Add Channels subgraph + mermaidCode += ` subgraph "Channels"\n`; + Object.entries(asyncAPIDocument.channels).forEach(([channelName, channelInfo]) => { + mermaidCode += ` ${channelName}["${channelName}"]\n`; + }); + mermaidCode += ` end\n`; + + // Add Operations subgraph + mermaidCode += ` subgraph "Operations"\n`; + Object.entries(asyncAPIDocument.operations).forEach(([operationName, operationInfo]) => { + mermaidCode += ` ${operationName}["${operationName}"]\n`; + }); + mermaidCode += ` end\n`; + + // Add Messages subgraph + mermaidCode += ` subgraph "Messages"\n`; + Object.entries(asyncAPIDocument.components.messages).forEach(([messageName, messageInfo]) => { + mermaidCode += ` ${messageName}["${messageName}"]\n`; + }); + mermaidCode += ` end\n`; + + mermaidCode += ` end\n`; + + // Add connections between servers and channels + Object.entries(asyncAPIDocument.servers).forEach(([serverName]) => { + Object.entries(asyncAPIDocument.channels).forEach(([channelName]) => { + mermaidCode += ` ${serverName} --> ${channelName}\n`; + }); + }); + + // Add connections between channels and operations + Object.entries(asyncAPIDocument.channels).forEach(([channelName, channelInfo]) => { + Object.entries(asyncAPIDocument.operations).forEach(([operationName]) => { + if (channelInfo.messages && channelInfo.messages[operationName]) { + mermaidCode += ` ${channelName} --> ${operationName}\n`; + } + }); + }); + + // Add connections between channels and messages + Object.entries(asyncAPIDocument.channels).forEach(([channelName, channelInfo]) => { + Object.entries(asyncAPIDocument.components.messages).forEach(([messageName]) => { + if (channelInfo.messages && channelInfo.messages[messageName]) { + mermaidCode += ` ${channelName} --> ${messageName}\n`; + } + }); + }); + + // Add connections between operations and messages + Object.entries(asyncAPIDocument.operations).forEach(([operationName, operationInfo]) => { + Object.entries(asyncAPIDocument.components.messages).forEach(([messageName]) => { + if (operationInfo.messages && operationInfo.messages[messageName]) { + mermaidCode += ` ${operationName} --> ${messageName}\n`; + } + }); + }); + return mermaidCode; +} diff --git a/src/PreviewWebPanel.ts b/src/PreviewWebPanel.ts index fabac06..233de51 100644 --- a/src/PreviewWebPanel.ts +++ b/src/PreviewWebPanel.ts @@ -1,5 +1,6 @@ import * as vscode from 'vscode'; import * as path from 'path'; +import { convertAsyncAPIToMermaid } from './ConvertMD'; let position : {x:0,y:0} = { x: 0, @@ -16,6 +17,7 @@ export function previewAsyncAPI(context: vscode.ExtensionContext) { }; } + export const openAsyncapiFiles: { [id: string]: vscode.WebviewPanel } = {}; // vscode.Uri.fsPath => vscode.WebviewPanel export function isAsyncAPIFile(document?: vscode.TextDocument) { @@ -56,8 +58,10 @@ export function openAsyncAPI(context: vscode.ExtensionContext, uri: vscode.Uri) }); panel.title = path.basename(uri.fsPath); + console.log('Opening HTML'); + console.log(getWebviewContent(context, panel.webview, uri, position)); panel.webview.html = getWebviewContent(context, panel.webview, uri, position); - + panel.webview.onDidReceiveMessage( message => { switch (message.type) { @@ -66,7 +70,6 @@ export function openAsyncAPI(context: vscode.ExtensionContext, uri: vscode.Uri) x: message.scrollX, y: message.scrollY }; - } } }, @@ -96,6 +99,23 @@ async function promptForAsyncapiFile() { return uris?.[0]; } +export function previewMarkdown(context: vscode.ExtensionContext) { + return "Hello World!"; + + } + + async function convertAsyncAPItoMD(context: vscode.ExtensionContext, uri: vscode.Uri) { + return "Hello World!"; + + } + +function convertToMD(context: vscode.ExtensionContext, webview: vscode.Webview, asyncapiFile: vscode.Uri, position: {x:0,y:0}){ + console.log('Converting to MD'); + const asyncapiWebviewUri = webview.asWebviewUri(asyncapiFile); + console.log(asyncapiWebviewUri); + return "Hello"; +} + function getWebviewContent(context: vscode.ExtensionContext, webview: vscode.Webview, asyncapiFile: vscode.Uri, position: {x:0,y:0}) { const asyncapiComponentJs = webview.asWebviewUri( vscode.Uri.joinPath(context.extensionUri, 'dist/node_modules/@asyncapi/react-component/browser/standalone/index.js') @@ -104,13 +124,14 @@ function getWebviewContent(context: vscode.ExtensionContext, webview: vscode.Web vscode.Uri.joinPath(context.extensionUri, 'dist/node_modules/@asyncapi/react-component/styles/default.min.css') ); const asyncapiWebviewUri = webview.asWebviewUri(asyncapiFile); + const mermaidCode = convertAsyncAPIToMermaid(asyncapiFile.fsPath); const asyncapiBasePath = asyncapiWebviewUri.toString().replace('%2B', '+'); // this is loaded by a different library so it requires unescaping the + character const html = ` - + -
- +
+        ${mermaidCode};
+      
- + `; diff --git a/src/extension.ts b/src/extension.ts index 55cfbfb..f2e7b1a 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,5 +1,5 @@ import * as vscode from 'vscode'; -import { isAsyncAPIFile, openAsyncAPI, openAsyncapiFiles, previewAsyncAPI } from './PreviewWebPanel'; +import { isAsyncAPIFile, openAsyncAPI, openAsyncapiFiles, previewAsyncAPI, previewMarkdown} from './PreviewWebPanel'; import { asyncapiSmartPaste } from './SmartPasteCommand'; @@ -37,6 +37,8 @@ export function activate(context: vscode.ExtensionContext) { context.subscriptions.push(vscode.commands.registerCommand('asyncapi.preview', previewAsyncAPI(context))); context.subscriptions.push(vscode.commands.registerCommand("asyncapi.paste", asyncapiSmartPaste)); + + context.subscriptions.push(vscode.commands.registerCommand('asyncapi.md', previewMarkdown(context))); } export function deactivate() {}