Skip to main content

Convert code to diagram with Miro PlantUML


The Miro PlantUML app leverages both the Miro Web SDK and REST API to demonstrate a Code > Diagram use case. More specifically, this app is based on the open-source Plant UML library used to draw UML diagrams from a plain text language. Leveraging this open-source library in conjunction with Miro, you can create diagrams from our Web SDK and REST API—including our V2 REST Connector endpoints for the lines that make up the diagrams.

We've included this app in our @miroapp repo because of its broad appeal to code to diagram use cases and its use of our REST API endpoints and Web SDK.

Figure 1. Code to diagram with Miro PlantUML.

This guide covers:

How the app works

This app is built primarily in Java/Kotlin and leverages JavaScript/Typescript for the frontend client components of the Miro Web SDK.

There are 3 main directories, located in /src/main: kotlin, resources, and webapp-sdk2.

  • The src/main/kotlin directory contains PlantUML packages (patched copy of PlantUML repository):
    • doc-files
    • ext/plantuml/com
    • gen
    • h
    • jcckit
    • net/sourceforge/plantuml
    • org/stathissideris/ascii2image
    • smetana/core
  • The src/main/kotlin/com/miro/miroappoauth directory contains the code for implementing Miro OAuth 2.0, such as the configuration, controllers, and service/helpers to retrieve tokens that can be leveraged by the app.
  • The src/main/resources directory contains Java config files such as HTTPS certs, application.yml, etc.
  • The src/main/webapp-sdk2 directory contains the code for the implementation of the Miro Web SDK V2 frontend of the application.
    • The /webapp-sdk2 directory contains the boilerplate app.html and index.html files for the Miro SDK
    • The src directory contains the main application.tsx and index.ts files for the frontend SDK application

Where is the open-source PlantUML component leveraged?

The PlantUML component is leveraged by the app after the code snippet input is provided by an end user to the Web SDK frontend’s text input:


Figure 2. The PlantUML code snippet input area in the modal of the app.

The PlantUML code is submitted via a controller in the CallRestRenderController.kt file, and the PlantUML code is ingested by the app via the following function:

@PostMapping("/submit-plantuml")
    fun submitPlantUml(
        @RequestHeader(HEADER_X_MIRO_TOKEN) jwtToken: String,
        @RequestBody submitPlantumlReq: SubmitPlantumlReq?
    ): String {
        val token = miroService.getTokenByJwtToken(jwtToken)
        renderService.render(token.accessTokenValue(), submitPlantumlReq!!)
        return "done"
    }

The code that is ingested by the app is then used to render a diagram in Miro. This rendering is executed by a service in the RenderService.kt file.

Where are the Miro REST API and SDK leveraged?

In the same RenderService.kt file above, the Miro Web SDK is leveraged to create shapes based on the code, and the REST API is called to create connectors (lines) to connect them and form the diagram.

The Web SDK is called in the following render function in RenderService.kt:

fun render(shape: ShapeWidget): String {}

The REST API’s V2 Connectors are leveraged in the following render function in RenderService.kt:

fun render(line: LineWidget, lineWidgetsId: LineWidgetsId): String {}

Note that each of these functions relies on the Miro REST API and Web SDK components in the following directory:

/src/main/kotlin/com/miro/miroappoauth/client/v2

To see the details of how the Miro REST API is called, see the supplementary README.md within the client/v2 folder:

src/main/kotlin/com/miro/miroappoauth/client/v2

Applying alternative 3rd-party sources to this use case

This app is based on the use case of ingesting PlantUML code to create diagrams in Miro. However, the same basic principles of this app can be applied to slightly different use cases as well.

Use case 1: app leverages both Miro’s REST API and Web SDK

If you’re interested in using this app as a starting point for a similar use case where both the Web SDK and the REST API are leveraged, we’ve listed specific files and dependencies that you need to work with.

In this scenario, an end user submits their own code from the frontend application in Miro, which leverages our Web SDK. The REST API is then used to construct the diagram in Miro, from the backend.

  1. First, you need to know where the Plant UML dependencies live in the code, and how they’re leveraged.

    As a reminder, the src/main/kotlin directory contains all of the PlantUML component dependencies:

    • doc-files
    • ext/plantuml/com
    • gen
    • h
    • jcckit
    • net/sourceforge/plantuml
    • org/stathissideris/ascii2image
    • smetana/core

    These dependencies will not be needed for use cases that don’t involve the open-source PlantUML component of this app.

  2. Second, you will need to understand where in the application the PlantUML is translated to the Miro REST API and Web SDK. This occurs in the CallRestRenderController.kt file referenced in the following section:

    Where is the open-source PlantUML component leveraged?

    An alternative data input or 3rd-party source needs to be ingested by the app in the same way as the PlantUML code.

    Your code must replace the controller that handles the PlantUML code and provide a new service to handle translating your data source to rendering via Miro’s REST API and Web SDK.

    The updates are needed in the following files where submitPlantUml is referenced:

    • src/main/kotlin/com/miro/miroappoauth/controllers/CallRestRenderController.kt
    • src/main/kotlin/com/miro/miroappoauth/services/RenderService.kt

    Instead of calling these PlantUML-related services and functions, the app must call a new service and reference this in the CallRestRenderController.kt file.

    Lastly, the Miro REST API and SDK components that are located in the client/v2 folder can remain largely unchanged. These files contain the code that formats the payloads for interaction with the REST API and Web SDK respectively:

    Image for REST API and SDK components located in the client/v2 folder
    Figure 3. REST API and SDK components located in the client/v2 folder.

Functional example

Let’s say that you’re creating an app that ingests Markdown language and translates it to diagrams in Miro.

Your code for handling the Markdown ingestion and translation would live in the kotlin directory, and the PlantUML-related services and functions would be replaced with your code, and would look something like this:

In services/RenderService.kt:

private fun parse(req: SubmitMarkdownReq) {
	<-- YOUR CODE HERE --> 
} 

Any dependencies of your new function would also need to live in the kotlin directory, instead of the PlantUML dependencies in this guide.

In controllers/CallRestRenderController.kt, the following functions are specific to PlantUML and must be replaced with code specific to your Markdown-related logic:

private fun formatPreviewUrl(payload: String): String {}
@PostMapping("/submit-plantuml")
    fun submitPlantUml(
        @RequestHeader(HEADER_X_MIRO_TOKEN) jwtToken: String,
        @RequestBody submitPlantumlReq: SubmitPlantumlReq?
    ): String {
        val token = miroService.getTokenByJwtToken(jwtToken)
        renderService.render(token.accessTokenValue(), submitPlantumlReq!!)
        return "done"
    }

Keep in mind that any logic you update could have an impact on other components, which you will need to take note of. For example, if you update functions that reference SubmitPlantUmlReq in any of your controllers, you also need to update the associated dto file in miroappoauth/dto/SubmitPlantumlReq.kt.

Use case 2: app leverages Miro REST API (ingested code is already known)

If you’re interested in using this app as a starting point for a use case where the code for the diagram is already known (or handled solely on the backend), and no frontend is needed (the Miro Web SDK), there are a few considerations to point out.

In this scenario, the code that the Miro diagram is based on will not come from an end user so the use of the Web SDK as the frontend of the app is not necessary. This use case assumes that the code you want to build a diagram from is already known. This might mean that:

  • Your app is importing code from a 3rd-party source or program, and storing it in your backend/database.

    Or

  • Your app is generating its own code that is then translated into a Miro diagram.

In either case above, the Miro REST API would then be used to construct a diagram in Miro, from the backend.

In the first scenario, you’re likely making requests to another 3rd-party source or program to retrieve the code you want to convert to a diagram in Miro. This action should happen on your backend, and the 3rd-party code you’re leveraging would be translated (like in the PlantUML case) in the CallRestRenderController.kt file referenced here.

The function submitPlantUml serves as a template for where you would apply the code your app is leveraging.

In the second scenario, the same applies in terms of not needing to leverage our frontend Web SDK. The difference between these two scenarios lies in the manner in which you’re generating or retrieving the code you’re leveraging (to ultimately create the diagram in Miro).

In the second scenario, it may not be necessary to store the code in a database—perhaps your logic for this will live in the controller or one of its associated services directly. This is something you will need to determine based on your specific use case.

In both cases, your code would likely need to update or replace the logic in the controller that handles the PlantUML code, as well as update or provide a new service to handle translating your data source to rendering via Miro’s REST API and Web SDK.

This requires changes in the following files where submitPlantUml is referenced:

src/main/kotlin/com/miro/miroappoauth/controllers/CallRestRenderController.kt
src/main/kotlin/com/miro/miroappoauth/services/RenderService.kt

It’s also possible that instead of calling these PlantUML-related services and functions, the app could call a new service and reference this in the CallRestRenderController.kt controller.

Conclusion

While this guide attempts to call out the most pertinent files and functions, there are undoubtedly functions, file names, and services that need to be updated depending on your specific implementation and the aspects of the app you want to change.

We hope this guide serves as a basis for making informed decisions and empowers you to create a code to diagram use case even more impressive than ours!