This topic describes how to extend Application Live View to support UI extensions.

Extensions allow having UI for the data produced by an app's actuator endpoint not covered by out-of-the-box UI. Therefore, an extension is an Angular application able to communicate with the server REST API. Creating an extension would require intermediate knowledge of Angular and the version of Angular must be 12 and above.

An extension is visually an extra navigation link(s) on the UI's instance page. The navigation link(s) show components from the extension displaying some actuator data from an app.

REST API

An extension is a way to present not covered actuator data nicely. The extension is likely to need an app's actuator endpoint as the REST API. The server has the actuator proxy endpoint which allows fetching actuator data from an app:

/api/instance/{id}/actuator/**

The id is an app instance id. After the id segment of the URL the usual app's actuator endpoint path segment follows, i.e. /instance/43af4-dff432-bfdf4/actuator/logfile is an example of /actuator/logfile endpoint for an with id 43af4-dff432-bfdf4.

Note: id is provided via the out-of-the-box UI (namely the parent route)

Creation of Angular app extension is likely to require testing it as a standalone web application. The app might require usage of the server endpoint fetching apps and app instances. The endpoint is:

/api/apps

It responds with JSON App[] where

interface App {
    name: string;
    instances: Instance[];
    bootVersion: string;
    buildVersion: string;
    health: string;
    appFlavour: string;
}

export interface Instance {
    id: string;
    appName: string;
    appFlavours: string[];
    url: string;
    bootVersion: string;
    buildVersion: string;
    health: string;
    actuatorHealthUrl: string;
    applicationUrl: string;
    actuatorPath: string;
}

Note: only the instance id likely to be the interesting piece for extension development.

UI

The UI is an Angular 12 or higher application that is taking advantage of Webpack 5 module federation feature. It allows pieces of one app to be compiled separately.

Basics

Create an Angular app (i.e. generate it using the CLI) where Angular version is 12 or higher. Create (or generate) a module with routing that would be hosting component(s) to be shown in the out-of-the-box UI. Create a proxy.conf.json file to point REST requests to the server:

{
    "/api/*": {
        "target": "http://localhost:5112",
        "secure": false,
        "logLevel": "debug",
        "pathRewrite": {
            "^/api": "/api"
        }
    }
}

Note: ng serve --configuration development --proxy-config proxy.conf.json to use redirects in dev mode.

The app should be able to be launched in dev mode and display the desired actuator data. Feel free to either hard-code the app instance id to an app instance available from the running server or derive it in the standalone bit of the extension app. (i.e. use /api/apps endpoint for example)

Activate and Configure Module Federation

Important: Going through the tutorial http://workshops.angulararchitects.io/ms/f4a0d303-ebe3-4fe8-9292-135bf3dce530/01b_module_federation.html first might be very helpful.

Use CLI with UI extension project root folder as the location:

ng add @angular-architects/module-federation --port 4200

Alternatively if there are more than one Angular project and port used to launch the app in dev mode is not 4200:

ng add @angular-architects/module-federation --project <extension project name> --port <port>

(Do not forget to Substitute values in the command above)

The above command will change a few files and generate few new ones. Among the generated files should be webpack.config.js Adjust its content as for the plugin rather than container (see comments in the file) rto something similar to the following:

const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");

[...]

module.exports = {
    [...],
    plugins: [
        new ModuleFederationPlugin({

            // For remotes (please adjust)
            name: "custom", // name of the extension
            filename: "remoteEntry.js",
            exposes: {
                // Update this:
                './Module': './projects/mfe1/src/app/custom/custom.module.ts',
            },        
            shared: {
                "@angular/core": { singleton: true, strictVersion: true }, 
                "@angular/common": { singleton: true, strictVersion: true }, 
                "@angular/router": { singleton: true, strictVersion: true },
                [...]
            }
        }),
        [...]
    ],
};

For more details on plugins with Webpack 5 Module Federation feature see:

  • Tutorial: http://workshops.angulararchitects.io/ms/f4a0d303-ebe3-4fe8-9292-135bf3dce530/01b_module_federation.html

  • Article: https://www.angulararchitects.io/aktuelles/the-microfrontend-revolution-part-2-module-federation-with-angular/

Now, once the Angular extension project is built the remoteEntry.js should be among the build artifacts in the dist folder.

logfile Extension Sample

The sample is located at /application-live-view-samples/plugins/logfile. Examine the source code to have a better understanding of the instructions above.

A few notes to the sample to run in standalone mode:

  1. Switch to RouterModule.forRoot(routes) in the logfile-routing.module.ts.

  2. Hardcode app instance id in instance.service.ts method getId(route: ActivatedRoute): Observable<string>

Hook Extension

There are a few steps needed to be done to hook plugins to the Application Live View server.

  1. Add extension manifest data to extensions YAML file

  2. Add navigation link data to app flavours YAML file

  3. Point the server to extensions YAML file and app flavours definition folder via spring boot properties:

Extension Manifest

There are three pieces of data required:

  1. entryPoint - URI or local Path of a extension "remote entry" JS file. The filename of the "remote entry" can be found in your extension webpack.config.js file. For example entryPoint can be a URL http://localhost:3000/remoteEntry.js or a local file path either absolute or relative to the extensions YAML file

  2. route - The route to be assigned in the UI to the extension. The route will be under /instance/ / . It is recommended to have route and name in webpack.config.js to be the same

  3. module - The name of the module being exposed in an extension (i.e. LogfileModule)

Example YAML snippet:

- route: logfile
  entryPoint: ./logfile/dist/logfile/remoteEntry.js
  module: LogfileModule

If there are more extensions there will be more YAML entries in the extensions YAML file like above.

Navigation Link

Data required to add navigation link for an extension is YAML snippet similar to below:

- type: link
  label: Log File
  icon: details
  route: logfile/log

Note that route property consists of two segment:

  • logfile is the route from the extension manifest data
  • log is the child route from logfile extension angular app routing module for which logfileComponent is shown

The above YAML snippet is to be added to the "App Flavour" YAML file. In order to override the spring-boot app flavour navigation links layout one would need to copy the file from the server resources folder and insert the extension navigation link YAML piece from the above. See contents of the /application-live-view-samples/plugins/flavours folder to get an idea how this needs to be done.

Point Server to Extensions and Flavours YAML Files

There should be two files created at this point:

  • extensions.yml containing YAML snippet(s) described in Extension Manifest section
  • flavours folder containing a spring-boot.yml file (or any other(s) <flavour>.yml) overriding corresponding app flavour to show links pointing to component(s) from extension

See examples:

  • /application-live-view-samples/plugins/application-live-view-plugins.yml - extensions YAML content
  • /application-live-view-samples/plugins/flavours- flavours definition folder, overrides spring-boot app flavour with spring-boot.yml file content

Now to launch the server with logfile extension start the server with these boot properties:

# Hook extensions YAML
app.live.view.server.plugins=./samples/plugins/application-live-view-plugins.yml
# Hook navigation link via spring-boot app flavour override
app.live.view.server.app-flavours-definition=./samples/plugins/flavours
# Set properties for /logfile actuator endpoint to work, thus App Live View server can be monitored by itself in this case
# Otherwise consider having these settings in spring boot apps monitored.
management.endpoint.logfile.external-file=/Users/me/alv-server.log
logging.file.name=/Users/me/alv-server.log
check-circle-line exclamation-circle-line close-line
Scroll to top icon