Microflow Call Hierarchy — Studio Pro Extension (Banner Image)
Microflow Call Hierarchy — Studio Pro Extension

Microflow Call Hierarchy - Studio Pro Extension

Jort van Gorkum
Mendix Community
Published in
8 min readAug 19, 2024

--

The extension is accomplished by using the newly available Mendix Studio Pro Extensions API. The API is written in C# and gives us multiple ways to customize the interaction with Studio Pro. For example, adding new items to the Studio Pro menu bar or adding a custom dockable pane that can show a WebView. This blog will describe how to implement a basic microflow call hierarchy extension using the API.

The goal of the Menditect extension

Our goal at Menditect was to create an extension to improve the user experience of creating tests by integrating MTA inside Mendix Studio Pro. We also wanted the extension to be useful for Mendix developers who cannot use MTA.

In MTA, there is a section called Mendix Model which gives an overview of what part of the Application under test is being tested. To be more specific, it specifies if and where a microflow is used as an action in a test. It is also possible to directly generate a test case with all the test steps needed to create the input data for executing the selected microflow.

Our idea was to make this feature available in Studio Pro. However, just showing a list of microflows with the indication that there is a test is not that useful compared to MTA. So, we needed a way to show the microflows in a more interesting way. One of those ways would be to show how a test would affect the flow of microflows (indirect coverage). This can be accomplished by using the call hierarchy.

The microflow call hierarchy shows what other microflows are called by a selected microflow in an easy-to-read tree structure. This also has the additional benefit that it provides insights for non-MTA users.

Building the Microflow Call Hierarchy extension

Managed Extensibility Framework (Dependency Injection)

Before we go into detail about how the extension works. We first need to go through how the Extensions API allows us to hook functionality into various areas of Mendix Studio Pro. The hooking functionality is implemented using the Managed Extensibility Framework or MEF. MEF provides a way to discover available components implicitly, via composition. An MEF component can specify its dependencies using imports (e.g., [ImportingConstructor]) and what it delivers using exports (e.g., [Export(…)]).

Using the MEF makes the process of adding custom components into Studio Pro easy. There is only one requirement because only specific components can be imported into Studio Pro. These are called extension points. These extension points are recognizable by the suffix Extension.

ContextMenuExtension

To start the process of using our microflow call hierarchy extension, we first need the user to select a microflow. The most usable option from the different extension points is the ContextMenuExtension. The ContextMenuExtension allows injecting new context menu (i.e., the right-click menu) items into model elements (e.g., microflows/entities).

Below is the code for injecting a context menu item “Microflow Call Hierarchy” in the Microflow element. When the context menu item is pressed an action is triggered which opens a custom pane MicroflowCallHierarchyPane. After opening the pane it posts a message to the webview of the pane with the selected microflow.

The interaction with tabs, panes, and editors is handled by the IDockingWindowService. A service in the Extension API is an interface that exposes some core Studio Pro functionality.

namespace Menditect.MendixExtensibility.MendixExtension.Blog;

using System.ComponentModel.Composition;
using Mendix.StudioPro.ExtensionsAPI.Model.Microflows;
using Mendix.StudioPro.ExtensionsAPI.UI.Menu;
using Mendix.StudioPro.ExtensionsAPI.UI.Services;

[Export(typeof(ContextMenuExtension<IMicroflow>))]
public class MicroflowCallHierarchyContextMenu : ContextMenuExtension<IMicroflow>
{
private readonly IDockingWindowService dockingWindowService;

[ImportingConstructor]
public MicroflowCallHierarchyContextMenu(IDockingWindowService dockingWindowService) {
this.dockingWindowService = dockingWindowService;
}

public override IEnumerable<MenuViewModel> GetContextMenus(IMicroflow element)
{
yield return new MenuViewModel(
"Microflow Call Hierarchy",
() =>
{
this.dockingWindowService.OpenPane(MicroflowCallHierarchyPane.Identifier);
MicroflowCallHierarchyPane.WebViewPane!.WebView!.PostMessage(
"active-microflow-id",
element.Id
);
}
);
}
}

The new context menu item “Microflow Call Hierarchy”

DockablePaneExtension & WebViewDockablePaneViewModel

When using the pane extension in the Extensions API, there are two classes needed: the DockablePaneExtension and the WebViewDockablePaneViewModel. The WebView is used as an in-application web content hoster using the Chromium-based Microsoft Edge rendering engine.

WebView

The webview makes it possible to view an entire website inside a pane of Studio Pro. There are two ways to make a local website available to the WebView:

  1. Using an HTML file
  2. Hosting the website on a port using a local server

For step one, there is an easy way to generate a HTML file from templates, by using a static site generator (SSG). A few popular SSG are Next.js, Hugo, Gatsby, Nuxt, etc. Depending on your familiarity with a frontend library/framework, choose a fitting tool. However, be warned, that some tools do not support client-side javascript execution. This means that only static (pre-compiled) information can be shown, so no dynamic content is possible with the website.

For step two, hosting the website using a server, there are enough options available to use. However, there are some drawbacks to using this method. First, you need to include the server runtime of the website in your extension. Then depending, on whether the server runtime needs dependencies, you also need to include those. For example, NextJS needs a runtime and Node.JS installed. This makes it more difficult to set up and less efficient than generating the HTML file beforehand.

Frontend

As stated in the previous section, when it comes to creating the HTML file there is an entire landscape of tools to choose from. For our Menditect extension, we have chosen to use Next.js. It is one of the most popular SSG tools, it uses React which we have the most experience with and it supports client-side javascript execution.

To share the data from the backend extension part to the frontend, we can use the message channel. It is a real-time way of updating the frontend state using messages without having to relaunch the webview. To receive a message in the front end, an event listener needs to be registered. For the creation of the event listener, we specify that we want to listen to the “message” event. When a message is received, the data is then checked and passed on to the state management system of the front end. Finally, when the event listener is added, we need to signal to the backend that the listener is registered. This is done by sending a message “MessageListenerRegistered” to the backend.

// Register event listener
window.chrome.webview.addEventListener("message", (event) => {
const { message, data } = event.data;

console.log("Received new message from backend", event.data);

if (message === "active-microflow-id" && typeof data === "string") {
// Update frontend state
setActiveMicroflowId(data);
}
});

// Indicate that we're ready to receive messages
window.chrome.webview.postMessage({ message: "MessageListenerRegistered" });

Web Server Extension & HTTP Requests

Now that the front end knows the microflow ID for which the call hierarchy needs to be computed. The front end needs to send a request to the backend to compute the hierarchy. There is the option to use the message event listener. However, the problem is that the data that is sent is unstructured and everyone that is listening to the message event receives it. A more structured way of sending/receiving messages is by using the WebServerExtension. This extension initializes an endpoint and processes every request sent to that endpoint. To define how a request needs to be handled is by adding routes to the given webserver.

Below is an example of creating a route for the microflow call hierarchy and adding it to the web server. First, we check if the request is the GET method. Second, we retrieve the microflowId from the request query and check if the microflowId is a valid microflow in the current application. Then we build the microflow call hierarchy, convert it to a JSON representation, and send it back with the response.

namespace Menditect.MendixExtensibility.MendixExtension.API;

using System.Text;
using Menditect.MendixExtensibility.MendixExtension.MicroflowCallHierarchy;
using Mendix.StudioPro.ExtensionsAPI.Model.Microflows;
using Mendix.StudioPro.ExtensionsAPI.UI.WebServer;
using Newtonsoft.Json;

public class MicroflowCallHierarchyWebServer : WebServerExtension
{
public override void InitializeWebServer(IWebServer webServer)
{
webServer.AddRoute(
"/microflow-call-hierarchy",
async (req, res, cancel) =>
{
if (req.HttpMethod == "GET")
{
string microflowId = req.QueryString["microflowId"];

if (
this.CurrentApp.TryGetAbstractUnitById(microflowId, out var abstractUnit)
&& abstractUnit is IMicroflow microflow
)
{
MicroflowNode rootNode = MicroflowCallHierarchyBuilder.build(microflowId);

string microflowCallHierarchyJson = JsonConvert.SerializeObject(rootNode);

byte[] bytes = Encoding.UTF8.GetBytes(microflowCallHierarchyJson);
res.StatusCode = 200;
res.StatusDescription = "OK";
res.ContentType = "application/json";
res.ContentEncoding = Encoding.UTF8;
res.ContentLength64 = bytes.Length;
res.OutputStream.Write(bytes, 0, bytes.Length);
}
}
}
);
}
}

Building the Microflow Call Hierarchy

To build the microflow call hierarchy, we create a builder that receives the IMicroflowService. The microflow service allows us to retrieve all the microflow activities from a given microflow. To determine the call hierarchy, we need to traverse from the given microflow through all its children. Think of it like traversing through a tree of nodes. So, given the children of the “rootNode” microflow, we compute the list of called microflows with the BuildChildNodes method. This is done by first filtering all the activities of the microflow, to check if they are a microflow call action. Then retrieve the called microflow and recursively call the parent method BuildNode again.

namespace Menditect.MendixExtensibility.MendixExtension.MicroflowCallHierarchy;

using Mendix.StudioPro.ExtensionsAPI.Model.Microflows;
using Mendix.StudioPro.ExtensionsAPI.Services;

public class MicroflowCallHierarchyBuilder
{
private readonly IMicroflowService microflowService;

public MicroflowCallHierarchyBuilder(IMicroflowService microflowService)
{
this.microflowService = microflowService;
}

private MicroflowNode BuildNode(IMicroflow microflow)
{
CallMicroflowNode microflowNode = new(microflow, this.BuildChildNodes(microflow));
return microflowNode;
}

private IEnumerable<MicroflowNode> BuildChildNodes(IMicroflow microflow)
{
return this
.microflowService.GetAllMicroflowActivities(microflow)
.OfType<IActionActivity>()
.Select(actionActivity => actionActivity.Action)
.OfType<IMicroflowCallAction>()
.Select(microflowCallAction =>
microflowCallAction.MicroflowCall.Microflow.Resolve()
)
.Where(microflow => microflow != null)
// Here is the recursive call
.Select(child => this.BuildNode(child));
}
}

There are two improvements to be made to this code: 1. It does not account for recursive microflows. This will lead to an indefinite computation of the hierarchy. 2. Multiple microflows can call the same microflow. Leading to unnecessary recomputation of the same call hierarchy. These improvements may be explained in a future installment of the blog.

Try it yourself!

You can learn more about the Extensions API at https://docs.mendix.com/apidocs-mxsdk/apidocs/extensibility-api/.

And if you already want to try the microflow call hierarchy, before creating it yourself. We already released a version you can try out at https://marketplace.mendix.com/link/component/225211

The Menditect Microflow Call Hierarchy Extension

Read more

From the Publisher -

Inspired by this article to bring your ideas to life with Mendix? Sign up for a free account! You’ll get instant access to the Mendix Academy, where you can start building your skills.

For more articles like this one, visit our Medium page. And you can find a wealth of instructional videos on our community YouTube page.

Speaking of our community, join us in our Slack community channel. We’d love to hear your ideas and insights!

--

--