Build a Custom Report

This article will explain how to build and publish reports for the LeanIX Store.

At one point you might discover, that the standard reports are not sufficient for your use cases in the LeanIX application. At that point, you have the option to create custom reports, reports which are tailored specifically for your needs.

To get started with custom reports, there are several tasks needed in advance. (See "What you need to get started")
Once you have a LeanIX workspace, an active API token, and you know the GraphQL API, you can directly follow the instructions which are listed below to create and publish custom reports.
All you need to have is a robust understanding of JavaScript and then you are ready to start.

Build your first custom report

Prerequisites

  • Node.js >= 8 (check with node -v)
  • npm >= 5 (check with npm -v)

Typedoc

Since the reporting library is created using TypeScript, we are able to provide documentation of the typings that you can find here: https://leanix.github.io/leanix-reporting/

Getting started

Install the package globally via npm:

npm install -g @leanix/reporting-cli

Initialize a new project:

mkdir myreport
cd myreport
lxr init
npm install

Configure your environment in lxr.json:

{
  "host": "app.leanix.net",
  "apitoken": "Jw8MfCqEXDDubry64H95SYYPjJTBKNFhkYD8kSCL"
}

If an API token is not available, you can also specify a workspace and log in with your workspace user:

{
  "host": "app.leanix.net",
  "workspace": "myworkspace"
}

Start developing:

npm start

Operating behind a proxy server

If you are operating behind a proxy simply add the "proxyURL" attribute to the lxr.json file as follows:

{
  "host": "app.leanix.net",
  "apitoken": "Jw8MfCqEXDDubry64H95SKYPjJTBKNFhkYD8kSCL",
  "proxyURL": "<add your proxy url here>"
}

📘

Host

If you have implemented SSO the host should be the domain for your workspace. The domain is visible in the url for your workspace.

If you are a US or EU customer and you do Not have SSO enabled. The host can be any of the following us.leanix.net, eu.leanix.net, or app.leanix.net. In order to determine which is the correct option, please look at the url for your workspace.

Security hint

When you run npm start a local webserver is hosted on localhost:8080 that allows connections via HTTPS. But since just a development SSL certificate is created the browser might show a warning that the connection is not secure.

You could either allow connections to this host anyways, or create your own self-signed certificate: https://www.tonyerwin.com/2014/09/generating-self-signed-ssl-certificates.html#MacKeyChainAccess

If you have created your certificate you can add the certificate and private key files to the lxr.json configuration file of your generated project:

{
  "host": "app.leanix.net",
  "apitoken": "Jw8MfCqEXDDubry64H95SKYPjJTBKNFhkYD8kSCL",
  "ssl": {
    "cert": "/path/to/cert",
    "key": "/path/to/key"
  }
}

For Windows users using IE11 or Microsoft Edge, follow these instructions.

Port of local dev server

By default the local dev server is hosted on port 8080. You can change that in your lxr.json via the "localPort" setting:

{
  "host": "app.leanix.net",
  ...
  "localPort": "4200"
}

Building your report

To build your report and distribute the result via some other way (instead of uploading it) you can run the following command:

lxr build

It will build your project and output the result into a dist folder.

  • package.json

This file contains information about your project. When uploading the report into LeanIX some of the information is used when displaying your report to the user.

Example package.json:

{
  "name": "leanix-quality-chart-report",
  "version": "1.0.0",
  "author": "LeanIX GmbH",
  "description": "This report shows the overall quality of your Fact Sheets per Fact Sheet type",
  "leanixReport": {
    "id": "net.leanix.quality-chart",
    "title": "Quality Chart",
    "documentationLink": "https://dev.leanix.net/example-docs",
    "defaultConfig": {
      "factSheetTypes": ["Application"]
    }
  }
}

Description of the properties that we extract from package.json when uploading your report to LeanIX:

  • name: Name for your report project

  • version: Version of your report

  • author: Creator of the report (optional)

  • description: Description of your reports use case (optional)

  • leanixReport: Additional properties used by LeanIX
    id: Unique ID for your report, we encourage you to follow Java package naming convention to force
    uniqueness(https://docs.oracle.com/javase/tutorial/java/package/namingpkgs.html) Note: the very last part of the id parameter is the one being used in the reporting menu. Please make sure it is unique so that it does not overwrite another report.
    title: Title for your report that is displayed in the GUI (optional)
    documentationLink: Link to a documentation of your report (optional)
    defaultConfig: Default configuration object, that can be adapted by the user (optional if your report
    is not configurable)

  • lxr.json

This file contains specific configuration for developing, testing and uploading your report. It is not meant to be tracked by version control, since it may contain user specific API Tokens. The file should be encoded in utf-8 to be processable by the reporting-cli.

  • src/index.html

This file is initially loaded by the LeanIX reporting framework and is hence the starting point for the execution of your report within the users browser.

  • src/index.js

This file is the starting point for bundling JavaScript into one file. You can use the import statement (https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Statements/import) in order to split up your project into multiple files. We use webpack 3.x under the hood to bundle your project.

Advanced build configuration

By default, the lxr build command will run node_modules/.bin/webpack to build your report and expect the dist folder to be called dist. If you want to change the dist folder or use another tool for building your report altogether, this can be configured using a leanixReportinCli section in package.json. The following example shows how you can configure your report to be built using parcel and a dist folder called output.

{
  "leanixReportingCli": {
    "distPath": "output",
    "buildCommand": "parcel"
  }
}

🚧

Note

Please note that the distPath will be removed before every build to ensure a clean build. However, using distPath will not automatically ensure that the build command actually uses this directory. Instead, the value used here must be aligned with the configuration of the build tool (parcel in the example above).

Core functionality of the reporting framework

In this section, you will get an overview of the key functionalities of the LeanIX Reporting Framework. As the standard reports are based on the same functionality, it is mandatory that all custom reports follow the same pattern to unify the user experience.

Filterable Fact Sheet information

You can configure the framework to fetch certain information of the Fact Sheets contained in a workspace. The user is able to apply filters to the set of Fact Sheets as he/she is used to from the LeanIX Inventory.

1181

In this example, we simply specify that we want to have the attributes displayName, type and description of Fact Sheets. The Reporting Framework will display GUI elements that allow the user to filter the set of Fact Sheets. Whenever the filter is changed by the user the callback function is invoked with the new set of data. Inside the callback, we create HTML based on the new data and display the generated HTML inside the body element.

import '@leanix/reporting';

lx.init()
.then(function (setupInfo) {
  var config = {
    facets: [{
      key: 'main',
      attributes: ['displayName', 'type', 'description'],
      callback: function (data) {
        console.log(data);
        var html = '';
        for (var index = 0; index < data.length; index++) {
          var fs = data[index];
          html += '<div>' + fs.displayName + '</div>';
        }
        document.body.innerHTML = html;
      }
    }]
  };
  lx.ready(config);
});

Custom Dropdowns

You can configure the framework to display custom dropdowns to the user and inform you, if he/she changes the selection.

The custom dropdown, named "SORT" can be seen below:

733

This example shows how to use custom dropdowns:

import '@leanix/reporting';

lx.init()
.then(function (setupInfo) {
  var config = {
    menuActions: {
      customDropdowns: [{
        id: 'SORT_DROPDOWN',
        name: 'SORT',
        entries: [
          {
            id: 'SORT_BY_NAME',
            name: 'By Name',
            callback: () => {
              console.log('Sort by name...');
            }
          },
          {
            id: 'SORT_BY_DATE',
            name: 'By Date',
            callback: () => {
              console.log('Sort by date...');
            }
          }
        ]
      }]
    }
  };
  lx.ready(config);
});

Views

You can use view information in your report, which allows you to classify Fact Sheets based on their attributes. The framework will display a dropdown menu with all available views for a specific Fact Sheet type. Whenever the user selects one of these views, the framework will pass the new view information down into your report, so that you can work with that information in your visualization.

1090

In this example we register a reportViewCallback with the config object. This tells the framework that we are interested in receiving view information. The framework will then show a dropdown to the user that allows him/her to select a view. The available views are determined by the reportViewFactSheetType.
The example code will log the background color for each Fact Sheet that is contained in the view information. In a real world scenario you would use these colours in the visualization of the report.

import '@leanix/reporting';

lx.init()
.then(function (setupInfo) {
  var config = {
    reportViewFactSheetType: 'Application',
    reportViewCallback: function (data) {
      // Build map from ledgendItem id to the item itself
      var ledgendItemMap = {};
      data.legendItems.forEach(item => { ledgendItemMap[item.id] = item; });

      // Print mappings
      data.mapping.map(fsMapping => {
        var ledgendItem = ledgendItemMap[fsMapping.legendId];
        console.log(`${fsMapping.fsId}: ledgendId=${fsMapping.legendId};bgColor=${ledgendItem.bgColor}`);
      });
    }
  };
  lx.ready(config);
});

Sample Report

The purpose of this segment is to provide a developer with an idea of how custom reports are usually structured. The example used is the default bootstrap report, which is explained in the How to build custom reports section of our documentation. The outcome of the report is going to be a report showing all Fact Sheets sorted by count (see below).

General concepts of web development are not going to be inside the scope of this section.

There are a number of steps required in the development process:

A. Define which functionality of the Reporting Framework you require

B. Fetching the data from the LeanIX application

C. Transform the data according to the desired result

D. Define how the transformed data is to be displayed

Review framework functionalities

To ensure consistency and make it easier to build reports the LeanIX Reporting Framework offers a set of functionalities that are useful for building reports. We therefore advise getting an overview of the functionalities of the Reporting Framework before getting into the development of the report.

Fetch data

The report config is the central element of each report. It defines the interaction between the report and the main application. At this point, you have the possibility of requesting data from your LeanIX workspace. There is also the possibility of directly interacting with the GraphQL API, but we recommend to request data from your LeanIX workspace directly.

createConfig() {
    return {
      menuActions: {
        customDropdowns: this.createDropdownConfig()
      },
      facets: [{
        key: 'main',
        attributes: ['displayName', 'type', 'description'],
        callback: function (data) {
          this.data = data;
          this.groups = _.groupBy(data, 'type');
          this.render();
        }.bind(this)
      }]
    };
  }

The following code snippet highlights the interaction between framework and report. The customDropdown interface signals to the main application to enable the dropdown menu, the logic for the dropdown comes from the report though.

createDropdownConfig() {
    return [{
      id: ID_SORTING_DROPDOWN,
      name: 'SORT',
      entries: [
        {
          id: ID_SORTING_BY_NAME,
          name: 'By Name',
          callback: () => {
            this.sorting = ID_SORTING_BY_NAME;
          this.render();
          }
        },
        {
          id: ID_SORTING_BY_COUNT,
          name: 'By Count',
          callback: () => {
            this.sorting = ID_SORTING_BY_COUNT;
            this.render();
          }
        }
      ]
    }];
  }

Transform data and deciding on a visualization

While usually logic and rendering are separate functions, in the following simple example they are in one function. Here you can see, how the fetched and grouped data is sorted and then passed to the HTML segment of the report.

render() {
    var fsTypes = _.keys(this.groups).sort(this.getSortComparer());
    var html = '<table>';
    for (var i = 0; i < fsTypes.length; i++) {
      html += this.getHtmlForFsTypeBar(fsTypes[i])
    }
    html += '</table>';
    html += '<div id="clickOutput"></div>';

    document.getElementById('report').innerHTML = html;

    $('.bar').on('click', (event) => {
      this.handleBarClick(event);
    });
  }

As a result, this example report shows a bar of each Fact Sheet type, sorted by count. How it is done is also described in the GraphQL in 10 minutes video tutorial.

882