Build solutions in ELMA365 / Portable services in modules

Portable services in modules

When the Low-code platform cannot provide the functionality needed for efficient integration of processes into the existing information landscape, we recommend creating custom microservices. These services can be used in a variety of ways and for the most demanding business needs.

Previously, we recommended installing services so that they are independent of the platform and supporting them on your own. This has a number of important drawbacks: services become hard to install and support for the developer and the administrator.

Solution of the issue

To make the development of microservices easier and faster, we’ve added a new feature to custom modules: you can now add services to them.

portable-microservices-1

A service is a Docker container that performs a certain functionality on the server. The platform wraps this container and places it in a separate space in the Kubernetes runtime environment.

начало внимание

Portable services are only available in ELMA365 On-Premises.

конец внимание

The Portable services option is available starting from ELMA365 On-Premises 2021.11. It is hidden behind the enableModuleServices feature flag. To toggle the feature on, use the following command on your ELMA365 On-Premises server:

elma365ctl featureflag enableModuleServices enable

Services and containers that a module can be installed to have a number of special properties and limitations:

  1. You cannot link a permanent repository to these containers, so all operations with the file system will be lost when the service is reset.  This means that the platform supports only stateless services. Nevertheless, they can access reliable external repositories.
  2. You can only add a container by specifying its URL. This means that you have to store the image of your service in a public or private repository before adding it. Moreover, the service image is not included in the .e365 package when the module is exported, so it cannot be transferred into another company. For now, when a standard module with services is installed, the system needs to have access to the specified repository URL to download the Docker image.
  3. These services have no resource limitations.
  4. Interaction with the service is only available from the module’s server scripts (widgets, processes, business process activities, API methods, event handlers) and only with HTTP requests. But the service itself can interact with any external service by any communication channels and protocols.

Read more about developing a custom service in the Guidelines for developing microservices for portable services article.

Use case

Let’s say we are developing a solution for a university, and we need to generate and display math formulas. We know that our users are proficient in the LaTeX markup language, so the task is to generate images from LaTeX formulas.

To do that, let’s find a ready-to-use container with a service that generates pictures from formulas.

You can set up this container on your own using the source code and place it in your private repository or use the version from the Docker Hub repository.

Create a service

portable-microservices-2

By default, the container image address is the URL of the Docker Hub repository. For a private repository, you can use an absolute URL and specify the login and token used to establish a secure connection. Note that the login and token are transmitted openly when the module is exported.

Use the service in a widget

Now we can use the service we created in a server script of a widget to generate images. Images can be generated with the GET /render HTTP method, as specified in the service’s description. So in the widget we are going to request a formula from the user (a String type field) and the desired width of the image (a Number type field).

Here is an example of a script you can write to use the service:

async function renderImage(): Promise<void> {
    let source = encodeURIComponent(Context.data.formula!);
    let width = encodeURIComponent((Context.data.image_width || 400).toString());
    let imgResp = await Namespace.services.math.fetch(`/render?input=latex&inline=1&output=png&source=${source}&width=${width}`, {
        headers: {
            "Accept" : "image/png"
        }
    });
 
    let buff = await imgResp.arrayBuffer();
 
    Context.data.formula_image = await Context.fields.formula_image.create(`render-${Date.now()}.png`, buff);
}

In this example we are using Namespace.services.math to access the service. This allows calling HTTP methods of the service in TS SDK server scripts. We specify the relative address of the method for the call, and the platform does the rest.

In the script itself, we are calling the service method, then getting a formula image as a buffer from the response, and saving it as a file into a variable of the File type. Now we have a widget that we can place on any page or form in the system:

portable-microservices-3

The rendered image will look like this:

portable-microservices-4

Found a typo? Highlight the text, press ctrl + enter and notify us